Fossil SCM
Merge trunk into forumpost-locking branch.
Commit
8e7de26aa2680f225080590781f5e2d6d82f99cd27cfdb8fd51aa5539a97ac9e
Parent
0af371047c7c426…
123 files changed
+20
-20
+1
-1
+957
-939
+17
-15
+594
-151
+2977
-1365
+80
-48
+6
-1
+5
+5
+19
-3
+14
-14
+14
-14
+87
+2
-2
+1
-1
+3
-3
+3
-1
+5
-3
+5
-3
+2
-2
+405
-65
+405
-65
+5
+5
+1
-1
+1
-1
+3
-3
+10
-10
+10
-10
+1
+43
+2
-2
+106
-29
+106
-29
+10
-9
+32
-33
+1
-1
+19
-19
+63
-37
+1
-1
+3
-3
+6
-19
+6
-4
+4
-4
+29
-9
+16
-16
+109
-15
+141
-124
+141
-124
+40
-40
+15
-15
+11
-11
+1
-1
+146
-77
+33
-33
+1
-1
+1
-1
+5
-5
+21
+13
+24
-24
+43
-10
+23
-18
+23
-18
+31
-32
+5
-13
+1
-1
+3
+18
-18
+18
-18
+12
-10
+1
-1
+13
-3
+6
-6
+5
-5
+15
-9
+1
-4
+2
-2
+25
-24
+1
-1
+5
-4
+1
-1
+56
-47
+52
-17
+22
-24
+184
-196
+22
-24
+14
-14
+1
-1
+38
-44
+6
-5
+1
-1
+1
-1
+60
-35
+27
-27
+70
-72
+168
-40
+63
-46
+19
-20
+7
-7
+52
-59
+1
-3
+2
-2
+1
-3
+68
-71
+18
-18
+122
-143
+2
-2
+3
-2
+4
-4
+66
-33
+9
-10
+2
-2
+22
-12
+2
-2
+49
-50
+14
-11
+191
-200
+42
-27
+10
-5
~
Dockerfile
~
VERSION
~
extsrc/pikchr.c
~
extsrc/pikchr.js
~
extsrc/pikchr.wasm
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
skins/ardoise/css.txt
~
skins/eagle/css.txt
~
skins/xekri/css.txt
~
src/alerts.c
~
src/attach.c
~
src/attach.c
~
src/blob.c
~
src/browse.c
~
src/cache.c
~
src/captcha.c
~
src/cgi.c
~
src/checkin.c
~
src/checkin.c
~
src/color.c
~
src/db.c
~
src/db.c
~
src/default.css
~
src/default.css
~
src/descendants.c
~
src/diff.c
~
src/dispatch.c
~
src/event.c
~
src/event.c
~
src/extcgi.c
~
src/file.c
~
src/finfo.c
~
src/forum.c
~
src/forum.c
~
src/glob.c
~
src/info.c
~
src/loadctrl.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/markdown_html.c
~
src/merge.c
~
src/patch.c
~
src/piechart.c
~
src/repolist.c
~
src/report.c
~
src/search.c
~
src/setup.c
~
src/setup.c
~
src/setupuser.c
~
src/shun.c
~
src/skins.c
~
src/sqlcmd.c
~
src/statrep.c
~
src/style.c
~
src/tag.c
~
src/tag.c
~
src/tar.c
~
src/th.c
~
src/th.h
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/tkt.c
~
src/tktsetup.c
~
src/update.c
~
src/user.c
~
src/vfile.c
~
src/wiki.c
~
src/wiki.c
~
src/wikiformat.c
~
src/winhttp.c
~
src/xfer.c
~
src/xfersetup.c
~
src/zip.c
~
tools/makemake.tcl
~
tools/mkindex.c
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
www/aboutcgi.wiki
~
www/adding_code.wiki
~
www/alerts.md
~
www/blockchain.md
~
www/build.wiki
~
www/changes.wiki
~
www/concepts.wiki
~
www/containers.md
~
www/custom_ticket.wiki
~
www/customskin.md
~
www/delta_encoder_algorithm.wiki
~
www/delta_format.wiki
~
www/encryptedrepos.wiki
~
www/faq.tcl
~
www/faq.wiki
~
www/fiveminutes.wiki
~
www/foss-cklist.wiki
~
www/fossil-v-git.wiki
~
www/gitusers.md
~
www/globs.md
~
www/image-format-vs-repo-size.md
~
www/index.wiki
~
www/mirrortogithub.md
~
www/mkindex.tcl
~
www/newrepo.wiki
~
www/permutedindex.html
~
www/pop.wiki
~
www/qandc.wiki
~
www/quickstart.wiki
~
www/quotes.wiki
~
www/rebaseharm.md
~
www/server/any/althttpd.md
~
www/server/debian/nginx.md
~
www/server/debian/service.md
~
www/server/index.html
~
www/server/whyuseaserver.wiki
~
www/settings.wiki
~
www/shunning.wiki
~
www/ssl.wiki
~
www/sync.wiki
~
www/unvers.wiki
~
www/wikitheory.wiki
+20
-20
| --- Dockerfile | ||
| +++ Dockerfile | ||
| @@ -8,12 +8,12 @@ | ||
| 8 | 8 | ### We aren't pinning to a more stable version of Alpine because we want |
| 9 | 9 | ### to build with the latest tools and libraries available in case they |
| 10 | 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | -FROM alpine:latest AS builder | |
| 14 | -WORKDIR /tmp | |
| 13 | +FROM alpine:latest AS bld | |
| 14 | +WORKDIR /fsl | |
| 15 | 15 | |
| 16 | 16 | ### Bake the basic Alpine Linux into a base layer so it only changes |
| 17 | 17 | ### when the upstream image is updated or we change the package set. |
| 18 | 18 | RUN set -x \ |
| 19 | 19 | && apk update \ |
| @@ -35,17 +35,17 @@ | ||
| 35 | 35 | ### by leveraging its DVCS nature via the "tarball" command and passing |
| 36 | 36 | ### the resulting file's name in. |
| 37 | 37 | ARG FSLCFG="" |
| 38 | 38 | ARG FSLVER="trunk" |
| 39 | 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | -ENV FSLSTB=/tmp/fsl/src.tar.gz | |
| 40 | +ENV FSLSTB=/fsl/src.tar.gz | |
| 41 | 41 | ADD $FSLURL $FSLSTB |
| 42 | 42 | RUN set -x \ |
| 43 | - && if [ -d $FSLSTB ] ; then mv $FSLSTB/src fsl ; \ | |
| 44 | - else tar -C fsl -xzf fsl/src.tar.gz ; fi \ | |
| 45 | - && m=fsl/src/src/main.mk \ | |
| 46 | - && fsl/src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j11 | |
| 43 | + && if [ -d $FSLSTB ] ; \ | |
| 44 | + then mv $FSLSTB/src . ; \ | |
| 45 | + else tar -xf src.tar.gz ; fi \ | |
| 46 | + && src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j16 | |
| 47 | 47 | |
| 48 | 48 | |
| 49 | 49 | ## --------------------------------------------------------------------- |
| 50 | 50 | ## STAGE 2: Pare that back to the bare essentials. |
| 51 | 51 | ## --------------------------------------------------------------------- |
| @@ -54,28 +54,28 @@ | ||
| 54 | 54 | ARG UID=499 |
| 55 | 55 | |
| 56 | 56 | ### Set up that base OS for our specific use without tying it to |
| 57 | 57 | ### anything likely to change often. So long as the user leaves |
| 58 | 58 | ### UID alone, this layer will be durable. |
| 59 | -RUN set -x \ | |
| 60 | - && mkdir log museum \ | |
| 61 | - && echo "root:x:0:0:Admin:/:/false" > /tmp/passwd \ | |
| 62 | - && echo "root:x:0:root" > /tmp/group \ | |
| 63 | - && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /tmp/passwd \ | |
| 64 | - && echo "fossil:x:${UID}:fossil" >> /tmp/group | |
| 59 | +RUN set -x \ | |
| 60 | + && mkdir e log museum \ | |
| 61 | + && echo "root:x:0:0:Admin:/:/false" > /e/passwd \ | |
| 62 | + && echo "root:x:0:root" > /e/group \ | |
| 63 | + && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /e/passwd \ | |
| 64 | + && echo "fossil:x:${UID}:fossil" >> /e/group | |
| 65 | 65 | |
| 66 | 66 | |
| 67 | 67 | ## --------------------------------------------------------------------- |
| 68 | 68 | ## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c |
| 69 | 69 | ## --------------------------------------------------------------------- |
| 70 | 70 | |
| 71 | 71 | FROM scratch AS run |
| 72 | -COPY --from=os /tmp/group /tmp/passwd /etc/ | |
| 73 | -COPY --from=os --chown=fossil:fossil /log /log/ | |
| 74 | -COPY --from=os --chown=fossil:fossil /museum /museum/ | |
| 75 | -COPY --from=os --chmod=1777 /tmp /tmp/ | |
| 76 | -COPY --from=builder /tmp/fossil /bin/ | |
| 72 | +COPY --from=bld --chmod=755 /fsl/fossil /bin/ | |
| 73 | +COPY --from=os --chmod=600 /e/* /etc/ | |
| 74 | +COPY --from=os --chmod=1777 /tmp /tmp/ | |
| 75 | +COPY --from=os --chown=fossil:fossil /log /log/ | |
| 76 | +COPY --from=os --chown=fossil:fossil /museum /museum/ | |
| 77 | 77 | |
| 78 | 78 | |
| 79 | 79 | ## --------------------------------------------------------------------- |
| 80 | 80 | ## RUN! |
| 81 | 81 | ## --------------------------------------------------------------------- |
| @@ -82,9 +82,9 @@ | ||
| 82 | 82 | |
| 83 | 83 | ENV PATH "/bin" |
| 84 | 84 | EXPOSE 8080/tcp |
| 85 | 85 | USER fossil |
| 86 | 86 | ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ] |
| 87 | -CMD [ \ | |
| 87 | +CMD [ \ | |
| 88 | 88 | "--create", \ |
| 89 | 89 | "--jsmode", "bundled", \ |
| 90 | - "--user", "admin" ] | |
| 90 | + "--user", "admin" ] | |
| 91 | 91 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -8,12 +8,12 @@ | |
| 8 | ### We aren't pinning to a more stable version of Alpine because we want |
| 9 | ### to build with the latest tools and libraries available in case they |
| 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | FROM alpine:latest AS builder |
| 14 | WORKDIR /tmp |
| 15 | |
| 16 | ### Bake the basic Alpine Linux into a base layer so it only changes |
| 17 | ### when the upstream image is updated or we change the package set. |
| 18 | RUN set -x \ |
| 19 | && apk update \ |
| @@ -35,17 +35,17 @@ | |
| 35 | ### by leveraging its DVCS nature via the "tarball" command and passing |
| 36 | ### the resulting file's name in. |
| 37 | ARG FSLCFG="" |
| 38 | ARG FSLVER="trunk" |
| 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | ENV FSLSTB=/tmp/fsl/src.tar.gz |
| 41 | ADD $FSLURL $FSLSTB |
| 42 | RUN set -x \ |
| 43 | && if [ -d $FSLSTB ] ; then mv $FSLSTB/src fsl ; \ |
| 44 | else tar -C fsl -xzf fsl/src.tar.gz ; fi \ |
| 45 | && m=fsl/src/src/main.mk \ |
| 46 | && fsl/src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j11 |
| 47 | |
| 48 | |
| 49 | ## --------------------------------------------------------------------- |
| 50 | ## STAGE 2: Pare that back to the bare essentials. |
| 51 | ## --------------------------------------------------------------------- |
| @@ -54,28 +54,28 @@ | |
| 54 | ARG UID=499 |
| 55 | |
| 56 | ### Set up that base OS for our specific use without tying it to |
| 57 | ### anything likely to change often. So long as the user leaves |
| 58 | ### UID alone, this layer will be durable. |
| 59 | RUN set -x \ |
| 60 | && mkdir log museum \ |
| 61 | && echo "root:x:0:0:Admin:/:/false" > /tmp/passwd \ |
| 62 | && echo "root:x:0:root" > /tmp/group \ |
| 63 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /tmp/passwd \ |
| 64 | && echo "fossil:x:${UID}:fossil" >> /tmp/group |
| 65 | |
| 66 | |
| 67 | ## --------------------------------------------------------------------- |
| 68 | ## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c |
| 69 | ## --------------------------------------------------------------------- |
| 70 | |
| 71 | FROM scratch AS run |
| 72 | COPY --from=os /tmp/group /tmp/passwd /etc/ |
| 73 | COPY --from=os --chown=fossil:fossil /log /log/ |
| 74 | COPY --from=os --chown=fossil:fossil /museum /museum/ |
| 75 | COPY --from=os --chmod=1777 /tmp /tmp/ |
| 76 | COPY --from=builder /tmp/fossil /bin/ |
| 77 | |
| 78 | |
| 79 | ## --------------------------------------------------------------------- |
| 80 | ## RUN! |
| 81 | ## --------------------------------------------------------------------- |
| @@ -82,9 +82,9 @@ | |
| 82 | |
| 83 | ENV PATH "/bin" |
| 84 | EXPOSE 8080/tcp |
| 85 | USER fossil |
| 86 | ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ] |
| 87 | CMD [ \ |
| 88 | "--create", \ |
| 89 | "--jsmode", "bundled", \ |
| 90 | "--user", "admin" ] |
| 91 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -8,12 +8,12 @@ | |
| 8 | ### We aren't pinning to a more stable version of Alpine because we want |
| 9 | ### to build with the latest tools and libraries available in case they |
| 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | FROM alpine:latest AS bld |
| 14 | WORKDIR /fsl |
| 15 | |
| 16 | ### Bake the basic Alpine Linux into a base layer so it only changes |
| 17 | ### when the upstream image is updated or we change the package set. |
| 18 | RUN set -x \ |
| 19 | && apk update \ |
| @@ -35,17 +35,17 @@ | |
| 35 | ### by leveraging its DVCS nature via the "tarball" command and passing |
| 36 | ### the resulting file's name in. |
| 37 | ARG FSLCFG="" |
| 38 | ARG FSLVER="trunk" |
| 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | ENV FSLSTB=/fsl/src.tar.gz |
| 41 | ADD $FSLURL $FSLSTB |
| 42 | RUN set -x \ |
| 43 | && if [ -d $FSLSTB ] ; \ |
| 44 | then mv $FSLSTB/src . ; \ |
| 45 | else tar -xf src.tar.gz ; fi \ |
| 46 | && src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j16 |
| 47 | |
| 48 | |
| 49 | ## --------------------------------------------------------------------- |
| 50 | ## STAGE 2: Pare that back to the bare essentials. |
| 51 | ## --------------------------------------------------------------------- |
| @@ -54,28 +54,28 @@ | |
| 54 | ARG UID=499 |
| 55 | |
| 56 | ### Set up that base OS for our specific use without tying it to |
| 57 | ### anything likely to change often. So long as the user leaves |
| 58 | ### UID alone, this layer will be durable. |
| 59 | RUN set -x \ |
| 60 | && mkdir e log museum \ |
| 61 | && echo "root:x:0:0:Admin:/:/false" > /e/passwd \ |
| 62 | && echo "root:x:0:root" > /e/group \ |
| 63 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /e/passwd \ |
| 64 | && echo "fossil:x:${UID}:fossil" >> /e/group |
| 65 | |
| 66 | |
| 67 | ## --------------------------------------------------------------------- |
| 68 | ## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c |
| 69 | ## --------------------------------------------------------------------- |
| 70 | |
| 71 | FROM scratch AS run |
| 72 | COPY --from=bld --chmod=755 /fsl/fossil /bin/ |
| 73 | COPY --from=os --chmod=600 /e/* /etc/ |
| 74 | COPY --from=os --chmod=1777 /tmp /tmp/ |
| 75 | COPY --from=os --chown=fossil:fossil /log /log/ |
| 76 | COPY --from=os --chown=fossil:fossil /museum /museum/ |
| 77 | |
| 78 | |
| 79 | ## --------------------------------------------------------------------- |
| 80 | ## RUN! |
| 81 | ## --------------------------------------------------------------------- |
| @@ -82,9 +82,9 @@ | |
| 82 | |
| 83 | ENV PATH "/bin" |
| 84 | EXPOSE 8080/tcp |
| 85 | USER fossil |
| 86 | ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ] |
| 87 | CMD [ \ |
| 88 | "--create", \ |
| 89 | "--jsmode", "bundled", \ |
| 90 | "--user", "admin" ] |
| 91 |
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.22 | |
| 1 | +2.23 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.22 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.23 |
| 2 |
+957
-939
| --- extsrc/pikchr.c | ||
| +++ extsrc/pikchr.c | ||
| @@ -59,11 +59,11 @@ | ||
| 59 | 59 | ** cc pikchr.c -o pikchr.o |
| 60 | 60 | ** |
| 61 | 61 | ** Add -DPIKCHR_SHELL to add a main() routine that reads input files |
| 62 | 62 | ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for |
| 63 | 63 | ** -fsanitizer=fuzzer testing. |
| 64 | -** | |
| 64 | +** | |
| 65 | 65 | **************************************************************************** |
| 66 | 66 | ** IMPLEMENTATION NOTES (for people who want to understand the internal |
| 67 | 67 | ** operation of this software, perhaps to extend the code or to fix bugs): |
| 68 | 68 | ** |
| 69 | 69 | ** Each call to pikchr() uses a single instance of the Pik structure to |
| @@ -203,12 +203,13 @@ | ||
| 203 | 203 | #define TP_SMALL 0x0200 /* Smaller font */ |
| 204 | 204 | #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ |
| 205 | 205 | #define TP_SZMASK 0x0700 /* Font size mask */ |
| 206 | 206 | #define TP_ITALIC 0x1000 /* Italic font */ |
| 207 | 207 | #define TP_BOLD 0x2000 /* Bold font */ |
| 208 | -#define TP_FMASK 0x3000 /* Mask for font style */ | |
| 209 | -#define TP_ALIGN 0x4000 /* Rotate to align with the line */ | |
| 208 | +#define TP_MONO 0x4000 /* Monospace font family */ | |
| 209 | +#define TP_FMASK 0x7000 /* Mask for font style */ | |
| 210 | +#define TP_ALIGN 0x8000 /* Rotate to align with the line */ | |
| 210 | 211 | |
| 211 | 212 | /* An object to hold a position in 2-D space */ |
| 212 | 213 | struct PPoint { |
| 213 | 214 | PNum x, y; /* X and Y coordinates */ |
| 214 | 215 | }; |
| @@ -470,11 +471,11 @@ | ||
| 470 | 471 | static void pik_bbox_init(PBox*); |
| 471 | 472 | static void pik_bbox_addbox(PBox*,PBox*); |
| 472 | 473 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 473 | 474 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 474 | 475 | static void pik_add_txt(Pik*,PToken*,int); |
| 475 | -static int pik_text_length(const PToken *pToken); | |
| 476 | +static int pik_text_length(const PToken *pToken, const int isMonospace); | |
| 476 | 477 | static void pik_size_to_fit(Pik*,PToken*,int); |
| 477 | 478 | static int pik_text_position(int,PToken*); |
| 478 | 479 | static PNum pik_property_of(PObj*,PToken*); |
| 479 | 480 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 480 | 481 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| @@ -489,11 +490,11 @@ | ||
| 489 | 490 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 490 | 491 | static PNum pik_dist(PPoint*,PPoint*); |
| 491 | 492 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 492 | 493 | |
| 493 | 494 | |
| 494 | -#line 520 "pikchr.c" | |
| 495 | +#line 521 "pikchr.c" | |
| 495 | 496 | /**************** End of %include directives **********************************/ |
| 496 | 497 | /* These constants specify the various numeric values for terminal symbols. |
| 497 | 498 | ***************** Begin token definitions *************************************/ |
| 498 | 499 | #ifndef T_ID |
| 499 | 500 | #define T_ID 1 |
| @@ -564,38 +565,39 @@ | ||
| 564 | 565 | #define T_RJUST 66 |
| 565 | 566 | #define T_ABOVE 67 |
| 566 | 567 | #define T_BELOW 68 |
| 567 | 568 | #define T_ITALIC 69 |
| 568 | 569 | #define T_BOLD 70 |
| 569 | -#define T_ALIGNED 71 | |
| 570 | -#define T_BIG 72 | |
| 571 | -#define T_SMALL 73 | |
| 572 | -#define T_AND 74 | |
| 573 | -#define T_LT 75 | |
| 574 | -#define T_GT 76 | |
| 575 | -#define T_ON 77 | |
| 576 | -#define T_WAY 78 | |
| 577 | -#define T_BETWEEN 79 | |
| 578 | -#define T_THE 80 | |
| 579 | -#define T_NTH 81 | |
| 580 | -#define T_VERTEX 82 | |
| 581 | -#define T_TOP 83 | |
| 582 | -#define T_BOTTOM 84 | |
| 583 | -#define T_START 85 | |
| 584 | -#define T_END 86 | |
| 585 | -#define T_IN 87 | |
| 586 | -#define T_THIS 88 | |
| 587 | -#define T_DOT_U 89 | |
| 588 | -#define T_LAST 90 | |
| 589 | -#define T_NUMBER 91 | |
| 590 | -#define T_FUNC1 92 | |
| 591 | -#define T_FUNC2 93 | |
| 592 | -#define T_DIST 94 | |
| 593 | -#define T_DOT_XY 95 | |
| 594 | -#define T_X 96 | |
| 595 | -#define T_Y 97 | |
| 596 | -#define T_DOT_L 98 | |
| 570 | +#define T_MONO 71 | |
| 571 | +#define T_ALIGNED 72 | |
| 572 | +#define T_BIG 73 | |
| 573 | +#define T_SMALL 74 | |
| 574 | +#define T_AND 75 | |
| 575 | +#define T_LT 76 | |
| 576 | +#define T_GT 77 | |
| 577 | +#define T_ON 78 | |
| 578 | +#define T_WAY 79 | |
| 579 | +#define T_BETWEEN 80 | |
| 580 | +#define T_THE 81 | |
| 581 | +#define T_NTH 82 | |
| 582 | +#define T_VERTEX 83 | |
| 583 | +#define T_TOP 84 | |
| 584 | +#define T_BOTTOM 85 | |
| 585 | +#define T_START 86 | |
| 586 | +#define T_END 87 | |
| 587 | +#define T_IN 88 | |
| 588 | +#define T_THIS 89 | |
| 589 | +#define T_DOT_U 90 | |
| 590 | +#define T_LAST 91 | |
| 591 | +#define T_NUMBER 92 | |
| 592 | +#define T_FUNC1 93 | |
| 593 | +#define T_FUNC2 94 | |
| 594 | +#define T_DIST 95 | |
| 595 | +#define T_DOT_XY 96 | |
| 596 | +#define T_X 97 | |
| 597 | +#define T_Y 98 | |
| 598 | +#define T_DOT_L 99 | |
| 597 | 599 | #endif |
| 598 | 600 | /**************** End token definitions ***************************************/ |
| 599 | 601 | |
| 600 | 602 | /* The next sections is a series of control #defines. |
| 601 | 603 | ** various aspects of the generated parser. |
| @@ -651,22 +653,22 @@ | ||
| 651 | 653 | #ifndef INTERFACE |
| 652 | 654 | # define INTERFACE 1 |
| 653 | 655 | #endif |
| 654 | 656 | /************* Begin control #defines *****************************************/ |
| 655 | 657 | #define YYCODETYPE unsigned char |
| 656 | -#define YYNOCODE 135 | |
| 658 | +#define YYNOCODE 136 | |
| 657 | 659 | #define YYACTIONTYPE unsigned short int |
| 658 | 660 | #define pik_parserTOKENTYPE PToken |
| 659 | 661 | typedef union { |
| 660 | 662 | int yyinit; |
| 661 | 663 | pik_parserTOKENTYPE yy0; |
| 662 | - PRel yy10; | |
| 663 | - PObj* yy36; | |
| 664 | - PPoint yy79; | |
| 665 | - PNum yy153; | |
| 666 | - short int yy164; | |
| 667 | - PList* yy227; | |
| 664 | + PNum yy21; | |
| 665 | + PPoint yy63; | |
| 666 | + PRel yy72; | |
| 667 | + PObj* yy162; | |
| 668 | + short int yy188; | |
| 669 | + PList* yy235; | |
| 668 | 670 | } YYMINORTYPE; |
| 669 | 671 | #ifndef YYSTACKDEPTH |
| 670 | 672 | #define YYSTACKDEPTH 100 |
| 671 | 673 | #endif |
| 672 | 674 | #define pik_parserARG_SDECL |
| @@ -681,11 +683,11 @@ | ||
| 681 | 683 | #define pik_parserCTX_STORE yypParser->p=p; |
| 682 | 684 | #define YYFALLBACK 1 |
| 683 | 685 | #define YYNSTATE 164 |
| 684 | 686 | #define YYNRULE 156 |
| 685 | 687 | #define YYNRULE_WITH_ACTION 116 |
| 686 | -#define YYNTOKEN 99 | |
| 688 | +#define YYNTOKEN 100 | |
| 687 | 689 | #define YY_MAX_SHIFT 163 |
| 688 | 690 | #define YY_MIN_SHIFTREDUCE 287 |
| 689 | 691 | #define YY_MAX_SHIFTREDUCE 442 |
| 690 | 692 | #define YY_ERROR_ACTION 443 |
| 691 | 693 | #define YY_ACCEPT_ACTION 444 |
| @@ -756,322 +758,324 @@ | ||
| 756 | 758 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 757 | 759 | ** shifting non-terminals after a reduce. |
| 758 | 760 | ** yy_default[] Default action for each state. |
| 759 | 761 | ** |
| 760 | 762 | *********** Begin parsing tables **********************************************/ |
| 761 | -#define YY_ACTTAB_COUNT (1303) | |
| 763 | +#define YY_ACTTAB_COUNT (1313) | |
| 762 | 764 | static const YYACTIONTYPE yy_action[] = { |
| 763 | 765 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 764 | 766 | /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, |
| 765 | 767 | /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, |
| 766 | 768 | /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, |
| 767 | 769 | /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, |
| 768 | 770 | /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, |
| 769 | 771 | /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, |
| 770 | - /* 70 */ 577, 73, 376, 148, 474, 533, 161, 119, 112, 113, | |
| 771 | - /* 80 */ 120, 161, 119, 128, 427, 428, 339, 357, 81, 531, | |
| 772 | - /* 90 */ 161, 119, 474, 36, 330, 13, 306, 322, 323, 9, | |
| 772 | + /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, | |
| 773 | + /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, | |
| 774 | + /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, | |
| 773 | 775 | /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, |
| 774 | - /* 110 */ 579, 310, 31, 339, 339, 339, 339, 425, 426, 340, | |
| 776 | + /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, | |
| 775 | 777 | /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, |
| 776 | - /* 130 */ 46, 59, 60, 64, 63, 62, 61, 54, 51, 376, | |
| 777 | - /* 140 */ 69, 108, 2, 47, 403, 83, 297, 435, 375, 84, | |
| 778 | - /* 150 */ 117, 80, 35, 308, 79, 133, 122, 126, 441, 440, | |
| 779 | - /* 160 */ 299, 123, 3, 404, 405, 406, 408, 80, 298, 308, | |
| 780 | - /* 170 */ 79, 4, 411, 412, 413, 414, 441, 440, 350, 350, | |
| 781 | - /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 62, 61, | |
| 782 | - /* 190 */ 67, 434, 1, 75, 378, 158, 74, 76, 148, 411, | |
| 783 | - /* 200 */ 412, 413, 414, 124, 113, 120, 161, 119, 106, 434, | |
| 784 | - /* 210 */ 436, 437, 438, 439, 5, 375, 6, 117, 393, 155, | |
| 785 | - /* 220 */ 154, 153, 394, 435, 69, 59, 60, 149, 436, 437, | |
| 786 | - /* 230 */ 438, 439, 535, 376, 398, 399, 2, 424, 427, 428, | |
| 787 | - /* 240 */ 339, 156, 156, 156, 423, 394, 435, 65, 59, 60, | |
| 788 | - /* 250 */ 162, 131, 441, 440, 397, 72, 376, 148, 118, 2, | |
| 789 | - /* 260 */ 380, 157, 125, 113, 120, 161, 119, 339, 339, 339, | |
| 790 | - /* 270 */ 339, 425, 426, 535, 11, 441, 440, 394, 356, 535, | |
| 791 | - /* 280 */ 59, 60, 535, 379, 159, 434, 149, 12, 102, 446, | |
| 792 | - /* 290 */ 432, 42, 138, 14, 435, 139, 301, 302, 303, 36, | |
| 793 | - /* 300 */ 305, 430, 106, 16, 436, 437, 438, 439, 434, 375, | |
| 794 | - /* 310 */ 18, 117, 393, 155, 154, 153, 44, 142, 140, 64, | |
| 795 | - /* 320 */ 63, 62, 61, 441, 440, 106, 19, 436, 437, 438, | |
| 796 | - /* 330 */ 439, 45, 375, 20, 117, 393, 155, 154, 153, 68, | |
| 797 | - /* 340 */ 55, 114, 64, 63, 62, 61, 147, 146, 394, 473, | |
| 798 | - /* 350 */ 359, 59, 60, 43, 23, 391, 434, 106, 26, 376, | |
| 799 | - /* 360 */ 57, 58, 42, 49, 375, 392, 117, 393, 155, 154, | |
| 800 | - /* 370 */ 153, 64, 63, 62, 61, 436, 437, 438, 439, 384, | |
| 801 | - /* 380 */ 382, 383, 22, 21, 377, 473, 160, 70, 39, 445, | |
| 802 | - /* 390 */ 24, 445, 145, 141, 431, 142, 140, 64, 63, 62, | |
| 803 | - /* 400 */ 61, 394, 15, 445, 59, 60, 64, 63, 62, 61, | |
| 804 | - /* 410 */ 391, 445, 376, 445, 445, 42, 445, 445, 55, 391, | |
| 805 | - /* 420 */ 156, 156, 156, 445, 147, 146, 445, 52, 106, 445, | |
| 806 | - /* 430 */ 445, 43, 445, 445, 445, 375, 445, 117, 393, 155, | |
| 807 | - /* 440 */ 154, 153, 445, 394, 143, 445, 59, 60, 64, 63, | |
| 808 | - /* 450 */ 62, 61, 313, 445, 376, 378, 158, 42, 445, 445, | |
| 809 | - /* 460 */ 22, 21, 121, 447, 454, 29, 445, 445, 24, 450, | |
| 810 | - /* 470 */ 145, 141, 431, 142, 140, 64, 63, 62, 61, 445, | |
| 811 | - /* 480 */ 163, 106, 445, 445, 444, 27, 445, 445, 375, 445, | |
| 812 | - /* 490 */ 117, 393, 155, 154, 153, 445, 55, 74, 445, 148, | |
| 813 | - /* 500 */ 445, 445, 147, 146, 497, 113, 120, 161, 119, 43, | |
| 814 | - /* 510 */ 445, 394, 445, 445, 59, 60, 445, 445, 445, 118, | |
| 815 | - /* 520 */ 445, 445, 376, 106, 445, 42, 445, 445, 149, 445, | |
| 816 | - /* 530 */ 375, 445, 117, 393, 155, 154, 153, 445, 22, 21, | |
| 817 | - /* 540 */ 394, 144, 445, 59, 60, 445, 24, 445, 145, 141, | |
| 818 | - /* 550 */ 431, 376, 445, 445, 42, 445, 132, 130, 394, 445, | |
| 819 | - /* 560 */ 445, 59, 60, 109, 447, 454, 29, 445, 445, 376, | |
| 820 | - /* 570 */ 450, 445, 42, 445, 394, 445, 445, 59, 60, 445, | |
| 821 | - /* 580 */ 445, 163, 445, 445, 445, 102, 27, 445, 42, 445, | |
| 822 | - /* 590 */ 445, 106, 445, 64, 63, 62, 61, 445, 375, 445, | |
| 823 | - /* 600 */ 117, 393, 155, 154, 153, 394, 355, 445, 59, 60, | |
| 824 | - /* 610 */ 445, 445, 445, 445, 445, 74, 376, 148, 445, 40, | |
| 825 | - /* 620 */ 106, 445, 496, 113, 120, 161, 119, 375, 445, 117, | |
| 826 | - /* 630 */ 393, 155, 154, 153, 445, 448, 454, 29, 106, 445, | |
| 827 | - /* 640 */ 445, 450, 445, 445, 445, 375, 149, 117, 393, 155, | |
| 828 | - /* 650 */ 154, 153, 163, 445, 106, 445, 445, 27, 445, 445, | |
| 829 | - /* 660 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, | |
| 830 | - /* 670 */ 445, 59, 60, 64, 63, 62, 61, 445, 445, 376, | |
| 831 | - /* 680 */ 445, 445, 41, 445, 445, 106, 354, 64, 63, 62, | |
| 832 | - /* 690 */ 61, 445, 375, 445, 117, 393, 155, 154, 153, 445, | |
| 833 | - /* 700 */ 445, 445, 74, 445, 148, 445, 88, 445, 445, 490, | |
| 834 | - /* 710 */ 113, 120, 161, 119, 445, 120, 161, 119, 17, 74, | |
| 835 | - /* 720 */ 445, 148, 110, 110, 445, 445, 484, 113, 120, 161, | |
| 836 | - /* 730 */ 119, 445, 445, 149, 74, 445, 148, 152, 445, 445, | |
| 837 | - /* 740 */ 445, 483, 113, 120, 161, 119, 445, 445, 106, 445, | |
| 838 | - /* 750 */ 149, 445, 445, 107, 445, 375, 445, 117, 393, 155, | |
| 839 | - /* 760 */ 154, 153, 120, 161, 119, 149, 478, 74, 445, 148, | |
| 840 | - /* 770 */ 445, 88, 445, 445, 480, 113, 120, 161, 119, 445, | |
| 841 | - /* 780 */ 120, 161, 119, 74, 152, 148, 10, 479, 479, 445, | |
| 842 | - /* 790 */ 134, 113, 120, 161, 119, 445, 445, 445, 149, 74, | |
| 843 | - /* 800 */ 445, 148, 152, 445, 445, 445, 517, 113, 120, 161, | |
| 844 | - /* 810 */ 119, 445, 445, 74, 149, 148, 445, 445, 445, 445, | |
| 845 | - /* 820 */ 137, 113, 120, 161, 119, 74, 445, 148, 445, 445, | |
| 846 | - /* 830 */ 149, 445, 525, 113, 120, 161, 119, 445, 74, 445, | |
| 847 | - /* 840 */ 148, 445, 445, 445, 149, 527, 113, 120, 161, 119, | |
| 848 | - /* 850 */ 445, 445, 74, 445, 148, 445, 149, 445, 445, 524, | |
| 849 | - /* 860 */ 113, 120, 161, 119, 74, 445, 148, 445, 445, 149, | |
| 850 | - /* 870 */ 445, 526, 113, 120, 161, 119, 445, 445, 74, 445, | |
| 851 | - /* 880 */ 148, 445, 88, 149, 445, 523, 113, 120, 161, 119, | |
| 852 | - /* 890 */ 445, 120, 161, 119, 74, 149, 148, 85, 111, 111, | |
| 853 | - /* 900 */ 445, 522, 113, 120, 161, 119, 120, 161, 119, 149, | |
| 854 | - /* 910 */ 74, 445, 148, 152, 445, 445, 445, 521, 113, 120, | |
| 855 | - /* 920 */ 161, 119, 445, 445, 74, 149, 148, 445, 152, 445, | |
| 856 | - /* 930 */ 445, 520, 113, 120, 161, 119, 74, 445, 148, 445, | |
| 857 | - /* 940 */ 445, 149, 445, 519, 113, 120, 161, 119, 445, 74, | |
| 858 | - /* 950 */ 445, 148, 445, 445, 445, 149, 150, 113, 120, 161, | |
| 859 | - /* 960 */ 119, 445, 445, 74, 445, 148, 445, 149, 445, 445, | |
| 860 | - /* 970 */ 151, 113, 120, 161, 119, 74, 445, 148, 445, 445, | |
| 861 | - /* 980 */ 149, 445, 136, 113, 120, 161, 119, 445, 445, 74, | |
| 862 | - /* 990 */ 445, 148, 107, 445, 149, 445, 135, 113, 120, 161, | |
| 863 | - /* 1000 */ 119, 120, 161, 119, 445, 463, 149, 445, 88, 445, | |
| 864 | - /* 1010 */ 445, 445, 78, 78, 445, 445, 107, 120, 161, 119, | |
| 865 | - /* 1020 */ 149, 445, 445, 152, 82, 120, 161, 119, 445, 463, | |
| 866 | - /* 1030 */ 445, 466, 86, 34, 445, 88, 445, 569, 445, 152, | |
| 867 | - /* 1040 */ 445, 120, 161, 119, 120, 161, 119, 152, 107, 445, | |
| 868 | - /* 1050 */ 445, 475, 64, 63, 62, 61, 445, 120, 161, 119, | |
| 869 | - /* 1060 */ 98, 451, 445, 152, 89, 396, 152, 90, 445, 120, | |
| 870 | - /* 1070 */ 161, 119, 445, 120, 161, 119, 120, 161, 119, 152, | |
| 871 | - /* 1080 */ 445, 64, 63, 62, 61, 445, 445, 445, 445, 445, | |
| 872 | - /* 1090 */ 87, 152, 445, 99, 395, 152, 100, 445, 152, 120, | |
| 873 | - /* 1100 */ 161, 119, 120, 161, 119, 120, 161, 119, 445, 101, | |
| 874 | - /* 1110 */ 64, 63, 62, 61, 445, 445, 445, 445, 120, 161, | |
| 875 | - /* 1120 */ 119, 152, 91, 391, 152, 445, 445, 152, 103, 445, | |
| 876 | - /* 1130 */ 445, 120, 161, 119, 445, 92, 445, 120, 161, 119, | |
| 877 | - /* 1140 */ 152, 93, 445, 445, 120, 161, 119, 104, 445, 445, | |
| 878 | - /* 1150 */ 120, 161, 119, 152, 445, 445, 120, 161, 119, 152, | |
| 879 | - /* 1160 */ 445, 445, 445, 445, 94, 445, 152, 445, 445, 445, | |
| 880 | - /* 1170 */ 105, 445, 152, 120, 161, 119, 445, 95, 152, 120, | |
| 881 | - /* 1180 */ 161, 119, 96, 445, 445, 445, 120, 161, 119, 445, | |
| 882 | - /* 1190 */ 445, 120, 161, 119, 97, 152, 445, 445, 445, 445, | |
| 883 | - /* 1200 */ 549, 152, 445, 120, 161, 119, 548, 445, 152, 120, | |
| 884 | - /* 1210 */ 161, 119, 445, 152, 445, 120, 161, 119, 445, 445, | |
| 885 | - /* 1220 */ 445, 445, 445, 547, 445, 152, 445, 445, 445, 445, | |
| 886 | - /* 1230 */ 445, 152, 120, 161, 119, 546, 445, 152, 445, 115, | |
| 887 | - /* 1240 */ 445, 445, 116, 445, 120, 161, 119, 445, 120, 161, | |
| 888 | - /* 1250 */ 119, 120, 161, 119, 152, 64, 63, 62, 61, 64, | |
| 889 | - /* 1260 */ 63, 62, 61, 445, 445, 445, 152, 445, 445, 445, | |
| 890 | - /* 1270 */ 152, 445, 445, 152, 445, 445, 50, 445, 445, 445, | |
| 891 | - /* 1280 */ 53, 64, 63, 62, 61, 445, 445, 445, 445, 445, | |
| 892 | - /* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 893 | - /* 1300 */ 445, 445, 56, | |
| 778 | + /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, | |
| 779 | + /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, | |
| 780 | + /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, | |
| 781 | + /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, | |
| 782 | + /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, | |
| 783 | + /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, | |
| 784 | + /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, | |
| 785 | + /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, | |
| 786 | + /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, | |
| 787 | + /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, | |
| 788 | + /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, | |
| 789 | + /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, | |
| 790 | + /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, | |
| 791 | + /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, | |
| 792 | + /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, | |
| 793 | + /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, | |
| 794 | + /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, | |
| 795 | + /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, | |
| 796 | + /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, | |
| 797 | + /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, | |
| 798 | + /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, | |
| 799 | + /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, | |
| 800 | + /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, | |
| 801 | + /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, | |
| 802 | + /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, | |
| 803 | + /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, | |
| 804 | + /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, | |
| 805 | + /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, | |
| 806 | + /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, | |
| 807 | + /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, | |
| 808 | + /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, | |
| 809 | + /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, | |
| 810 | + /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, | |
| 811 | + /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, | |
| 812 | + /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, | |
| 813 | + /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, | |
| 814 | + /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, | |
| 815 | + /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, | |
| 816 | + /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, | |
| 817 | + /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, | |
| 818 | + /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, | |
| 819 | + /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, | |
| 820 | + /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, | |
| 821 | + /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, | |
| 822 | + /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, | |
| 823 | + /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, | |
| 824 | + /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, | |
| 825 | + /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, | |
| 826 | + /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, | |
| 827 | + /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, | |
| 828 | + /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, | |
| 829 | + /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, | |
| 830 | + /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, | |
| 831 | + /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, | |
| 832 | + /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, | |
| 833 | + /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, | |
| 834 | + /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, | |
| 835 | + /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, | |
| 836 | + /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, | |
| 837 | + /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, | |
| 838 | + /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, | |
| 839 | + /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, | |
| 840 | + /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, | |
| 841 | + /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, | |
| 842 | + /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, | |
| 843 | + /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, | |
| 844 | + /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, | |
| 845 | + /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, | |
| 846 | + /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, | |
| 847 | + /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, | |
| 848 | + /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, | |
| 849 | + /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, | |
| 850 | + /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, | |
| 851 | + /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, | |
| 852 | + /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, | |
| 853 | + /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, | |
| 854 | + /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, | |
| 855 | + /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, | |
| 856 | + /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, | |
| 857 | + /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, | |
| 858 | + /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, | |
| 859 | + /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, | |
| 860 | + /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, | |
| 861 | + /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, | |
| 862 | + /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, | |
| 863 | + /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, | |
| 864 | + /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, | |
| 865 | + /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, | |
| 866 | + /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, | |
| 867 | + /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, | |
| 868 | + /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, | |
| 869 | + /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, | |
| 870 | + /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, | |
| 871 | + /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, | |
| 872 | + /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, | |
| 873 | + /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, | |
| 874 | + /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, | |
| 875 | + /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, | |
| 876 | + /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, | |
| 877 | + /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, | |
| 878 | + /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, | |
| 879 | + /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, | |
| 880 | + /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, | |
| 881 | + /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, | |
| 882 | + /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, | |
| 883 | + /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, | |
| 884 | + /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, | |
| 885 | + /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, | |
| 886 | + /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, | |
| 887 | + /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, | |
| 888 | + /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, | |
| 889 | + /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, | |
| 890 | + /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 891 | + /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, | |
| 892 | + /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, | |
| 893 | + /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 894 | + /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, | |
| 895 | + /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 896 | + /* 1310 */ 445, 445, 152, | |
| 894 | 897 | }; |
| 895 | 898 | static const YYCODETYPE yy_lookahead[] = { |
| 896 | - /* 0 */ 0, 112, 113, 114, 133, 101, 102, 103, 105, 105, | |
| 897 | - /* 10 */ 10, 112, 113, 114, 110, 111, 112, 113, 114, 105, | |
| 898 | - /* 20 */ 20, 21, 22, 104, 24, 125, 107, 108, 28, 4, | |
| 899 | - /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 134, 39, | |
| 900 | - /* 40 */ 40, 41, 42, 104, 44, 45, 107, 108, 106, 49, | |
| 899 | + /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, | |
| 900 | + /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, | |
| 901 | + /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, | |
| 902 | + /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, | |
| 903 | + /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, | |
| 901 | 904 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 902 | - /* 60 */ 60, 61, 62, 63, 0, 112, 113, 114, 129, 130, | |
| 903 | - /* 70 */ 131, 103, 12, 105, 10, 112, 113, 114, 110, 111, | |
| 904 | - /* 80 */ 112, 113, 114, 105, 20, 21, 22, 17, 24, 112, | |
| 905 | - /* 90 */ 113, 114, 28, 10, 2, 25, 25, 33, 34, 35, | |
| 906 | - /* 100 */ 36, 37, 134, 39, 40, 41, 42, 2, 44, 45, | |
| 907 | - /* 110 */ 132, 28, 127, 49, 50, 51, 52, 53, 54, 55, | |
| 905 | + /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, | |
| 906 | + /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, | |
| 907 | + /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, | |
| 908 | + /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, | |
| 909 | + /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, | |
| 910 | + /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, | |
| 908 | 911 | /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, |
| 909 | - /* 130 */ 38, 4, 5, 4, 5, 6, 7, 4, 5, 12, | |
| 910 | - /* 140 */ 3, 81, 15, 38, 1, 115, 17, 2, 88, 115, | |
| 911 | - /* 150 */ 90, 24, 128, 26, 27, 12, 1, 14, 31, 32, | |
| 912 | - /* 160 */ 19, 18, 16, 20, 21, 22, 23, 24, 17, 26, | |
| 913 | - /* 170 */ 27, 15, 29, 30, 31, 32, 31, 32, 64, 65, | |
| 914 | - /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 6, 7, | |
| 915 | - /* 190 */ 43, 64, 13, 48, 26, 27, 103, 48, 105, 29, | |
| 916 | - /* 200 */ 30, 31, 32, 110, 111, 112, 113, 114, 81, 64, | |
| 917 | - /* 210 */ 83, 84, 85, 86, 40, 88, 40, 90, 91, 92, | |
| 918 | - /* 220 */ 93, 94, 1, 2, 87, 4, 5, 134, 83, 84, | |
| 919 | - /* 230 */ 85, 86, 48, 12, 96, 97, 15, 41, 20, 21, | |
| 920 | - /* 240 */ 22, 20, 21, 22, 41, 1, 2, 98, 4, 5, | |
| 921 | - /* 250 */ 82, 47, 31, 32, 17, 103, 12, 105, 90, 15, | |
| 922 | - /* 260 */ 26, 27, 110, 111, 112, 113, 114, 49, 50, 51, | |
| 923 | - /* 270 */ 52, 53, 54, 89, 25, 31, 32, 1, 17, 95, | |
| 924 | - /* 280 */ 4, 5, 98, 26, 27, 64, 134, 74, 12, 0, | |
| 925 | - /* 290 */ 79, 15, 78, 3, 2, 80, 20, 21, 22, 10, | |
| 926 | - /* 300 */ 24, 79, 81, 3, 83, 84, 85, 86, 64, 88, | |
| 927 | - /* 310 */ 3, 90, 91, 92, 93, 94, 38, 2, 3, 4, | |
| 928 | - /* 320 */ 5, 6, 7, 31, 32, 81, 3, 83, 84, 85, | |
| 929 | - /* 330 */ 86, 16, 88, 3, 90, 91, 92, 93, 94, 3, | |
| 930 | - /* 340 */ 25, 95, 4, 5, 6, 7, 31, 32, 1, 2, | |
| 931 | - /* 350 */ 76, 4, 5, 38, 25, 17, 64, 81, 15, 12, | |
| 932 | - /* 360 */ 15, 15, 15, 25, 88, 17, 90, 91, 92, 93, | |
| 933 | - /* 370 */ 94, 4, 5, 6, 7, 83, 84, 85, 86, 28, | |
| 934 | - /* 380 */ 28, 28, 67, 68, 12, 38, 89, 3, 11, 135, | |
| 935 | - /* 390 */ 75, 135, 77, 78, 79, 2, 3, 4, 5, 6, | |
| 936 | - /* 400 */ 7, 1, 35, 135, 4, 5, 4, 5, 6, 7, | |
| 937 | - /* 410 */ 17, 135, 12, 135, 135, 15, 135, 135, 25, 17, | |
| 938 | - /* 420 */ 20, 21, 22, 135, 31, 32, 135, 25, 81, 135, | |
| 939 | - /* 430 */ 135, 38, 135, 135, 135, 88, 135, 90, 91, 92, | |
| 940 | - /* 440 */ 93, 94, 135, 1, 2, 135, 4, 5, 4, 5, | |
| 941 | - /* 450 */ 6, 7, 8, 135, 12, 26, 27, 15, 135, 135, | |
| 942 | - /* 460 */ 67, 68, 99, 100, 101, 102, 135, 135, 75, 106, | |
| 943 | - /* 470 */ 77, 78, 79, 2, 3, 4, 5, 6, 7, 135, | |
| 944 | - /* 480 */ 117, 81, 135, 135, 121, 122, 135, 135, 88, 135, | |
| 945 | - /* 490 */ 90, 91, 92, 93, 94, 135, 25, 103, 135, 105, | |
| 946 | - /* 500 */ 135, 135, 31, 32, 110, 111, 112, 113, 114, 38, | |
| 947 | - /* 510 */ 135, 1, 135, 135, 4, 5, 135, 135, 135, 90, | |
| 948 | - /* 520 */ 135, 135, 12, 81, 135, 15, 135, 135, 134, 135, | |
| 949 | - /* 530 */ 88, 135, 90, 91, 92, 93, 94, 135, 67, 68, | |
| 950 | - /* 540 */ 1, 2, 135, 4, 5, 135, 75, 135, 77, 78, | |
| 951 | - /* 550 */ 79, 12, 135, 135, 15, 135, 46, 47, 1, 135, | |
| 952 | - /* 560 */ 135, 4, 5, 99, 100, 101, 102, 135, 135, 12, | |
| 953 | - /* 570 */ 106, 135, 15, 135, 1, 135, 135, 4, 5, 135, | |
| 954 | - /* 580 */ 135, 117, 135, 135, 135, 12, 122, 135, 15, 135, | |
| 955 | - /* 590 */ 135, 81, 135, 4, 5, 6, 7, 135, 88, 135, | |
| 956 | - /* 600 */ 90, 91, 92, 93, 94, 1, 17, 135, 4, 5, | |
| 957 | - /* 610 */ 135, 135, 135, 135, 135, 103, 12, 105, 135, 15, | |
| 958 | - /* 620 */ 81, 135, 110, 111, 112, 113, 114, 88, 135, 90, | |
| 959 | - /* 630 */ 91, 92, 93, 94, 135, 100, 101, 102, 81, 135, | |
| 960 | - /* 640 */ 135, 106, 135, 135, 135, 88, 134, 90, 91, 92, | |
| 961 | - /* 650 */ 93, 94, 117, 135, 81, 135, 135, 122, 135, 135, | |
| 962 | - /* 660 */ 135, 88, 135, 90, 91, 92, 93, 94, 1, 135, | |
| 963 | - /* 670 */ 135, 4, 5, 4, 5, 6, 7, 135, 135, 12, | |
| 964 | - /* 680 */ 135, 135, 15, 135, 135, 81, 17, 4, 5, 6, | |
| 965 | - /* 690 */ 7, 135, 88, 135, 90, 91, 92, 93, 94, 135, | |
| 966 | - /* 700 */ 135, 135, 103, 135, 105, 135, 103, 135, 135, 110, | |
| 967 | - /* 710 */ 111, 112, 113, 114, 135, 112, 113, 114, 35, 103, | |
| 968 | - /* 720 */ 135, 105, 119, 120, 135, 135, 110, 111, 112, 113, | |
| 969 | - /* 730 */ 114, 135, 135, 134, 103, 135, 105, 134, 135, 135, | |
| 970 | - /* 740 */ 135, 110, 111, 112, 113, 114, 135, 135, 81, 135, | |
| 971 | - /* 750 */ 134, 135, 135, 103, 135, 88, 135, 90, 91, 92, | |
| 972 | - /* 760 */ 93, 94, 112, 113, 114, 134, 116, 103, 135, 105, | |
| 973 | - /* 770 */ 135, 103, 135, 135, 110, 111, 112, 113, 114, 135, | |
| 974 | - /* 780 */ 112, 113, 114, 103, 134, 105, 118, 119, 120, 135, | |
| 975 | - /* 790 */ 110, 111, 112, 113, 114, 135, 135, 135, 134, 103, | |
| 976 | - /* 800 */ 135, 105, 134, 135, 135, 135, 110, 111, 112, 113, | |
| 977 | - /* 810 */ 114, 135, 135, 103, 134, 105, 135, 135, 135, 135, | |
| 978 | - /* 820 */ 110, 111, 112, 113, 114, 103, 135, 105, 135, 135, | |
| 979 | - /* 830 */ 134, 135, 110, 111, 112, 113, 114, 135, 103, 135, | |
| 980 | - /* 840 */ 105, 135, 135, 135, 134, 110, 111, 112, 113, 114, | |
| 981 | - /* 850 */ 135, 135, 103, 135, 105, 135, 134, 135, 135, 110, | |
| 982 | - /* 860 */ 111, 112, 113, 114, 103, 135, 105, 135, 135, 134, | |
| 983 | - /* 870 */ 135, 110, 111, 112, 113, 114, 135, 135, 103, 135, | |
| 984 | - /* 880 */ 105, 135, 103, 134, 135, 110, 111, 112, 113, 114, | |
| 985 | - /* 890 */ 135, 112, 113, 114, 103, 134, 105, 103, 119, 120, | |
| 986 | - /* 900 */ 135, 110, 111, 112, 113, 114, 112, 113, 114, 134, | |
| 987 | - /* 910 */ 103, 135, 105, 134, 135, 135, 135, 110, 111, 112, | |
| 988 | - /* 920 */ 113, 114, 135, 135, 103, 134, 105, 135, 134, 135, | |
| 989 | - /* 930 */ 135, 110, 111, 112, 113, 114, 103, 135, 105, 135, | |
| 990 | - /* 940 */ 135, 134, 135, 110, 111, 112, 113, 114, 135, 103, | |
| 991 | - /* 950 */ 135, 105, 135, 135, 135, 134, 110, 111, 112, 113, | |
| 992 | - /* 960 */ 114, 135, 135, 103, 135, 105, 135, 134, 135, 135, | |
| 993 | - /* 970 */ 110, 111, 112, 113, 114, 103, 135, 105, 135, 135, | |
| 994 | - /* 980 */ 134, 135, 110, 111, 112, 113, 114, 135, 135, 103, | |
| 995 | - /* 990 */ 135, 105, 103, 135, 134, 135, 110, 111, 112, 113, | |
| 996 | - /* 1000 */ 114, 112, 113, 114, 135, 116, 134, 135, 103, 135, | |
| 997 | - /* 1010 */ 135, 135, 123, 124, 135, 135, 103, 112, 113, 114, | |
| 998 | - /* 1020 */ 134, 135, 135, 134, 119, 112, 113, 114, 135, 116, | |
| 999 | - /* 1030 */ 135, 126, 103, 128, 135, 103, 135, 124, 135, 134, | |
| 1000 | - /* 1040 */ 135, 112, 113, 114, 112, 113, 114, 134, 103, 135, | |
| 1001 | - /* 1050 */ 135, 119, 4, 5, 6, 7, 135, 112, 113, 114, | |
| 1002 | - /* 1060 */ 103, 116, 135, 134, 103, 17, 134, 103, 135, 112, | |
| 1003 | - /* 1070 */ 113, 114, 135, 112, 113, 114, 112, 113, 114, 134, | |
| 1004 | - /* 1080 */ 135, 4, 5, 6, 7, 135, 135, 135, 135, 135, | |
| 1005 | - /* 1090 */ 103, 134, 135, 103, 17, 134, 103, 135, 134, 112, | |
| 1006 | - /* 1100 */ 113, 114, 112, 113, 114, 112, 113, 114, 135, 103, | |
| 1007 | - /* 1110 */ 4, 5, 6, 7, 135, 135, 135, 135, 112, 113, | |
| 1008 | - /* 1120 */ 114, 134, 103, 17, 134, 135, 135, 134, 103, 135, | |
| 1009 | - /* 1130 */ 135, 112, 113, 114, 135, 103, 135, 112, 113, 114, | |
| 1010 | - /* 1140 */ 134, 103, 135, 135, 112, 113, 114, 103, 135, 135, | |
| 1011 | - /* 1150 */ 112, 113, 114, 134, 135, 135, 112, 113, 114, 134, | |
| 1012 | - /* 1160 */ 135, 135, 135, 135, 103, 135, 134, 135, 135, 135, | |
| 1013 | - /* 1170 */ 103, 135, 134, 112, 113, 114, 135, 103, 134, 112, | |
| 1014 | - /* 1180 */ 113, 114, 103, 135, 135, 135, 112, 113, 114, 135, | |
| 1015 | - /* 1190 */ 135, 112, 113, 114, 103, 134, 135, 135, 135, 135, | |
| 1016 | - /* 1200 */ 103, 134, 135, 112, 113, 114, 103, 135, 134, 112, | |
| 1017 | - /* 1210 */ 113, 114, 135, 134, 135, 112, 113, 114, 135, 135, | |
| 1018 | - /* 1220 */ 135, 135, 135, 103, 135, 134, 135, 135, 135, 135, | |
| 1019 | - /* 1230 */ 135, 134, 112, 113, 114, 103, 135, 134, 135, 103, | |
| 1020 | - /* 1240 */ 135, 135, 103, 135, 112, 113, 114, 135, 112, 113, | |
| 1021 | - /* 1250 */ 114, 112, 113, 114, 134, 4, 5, 6, 7, 4, | |
| 1022 | - /* 1260 */ 5, 6, 7, 135, 135, 135, 134, 135, 135, 135, | |
| 1023 | - /* 1270 */ 134, 135, 135, 134, 135, 135, 25, 135, 135, 135, | |
| 1024 | - /* 1280 */ 25, 4, 5, 6, 7, 135, 135, 135, 135, 135, | |
| 1025 | - /* 1290 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1026 | - /* 1300 */ 135, 135, 25, 135, 135, 135, 135, 135, 135, 135, | |
| 1027 | - /* 1310 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1028 | - /* 1320 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1029 | - /* 1330 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1030 | - /* 1340 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1031 | - /* 1350 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1032 | - /* 1360 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1033 | - /* 1370 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, | |
| 1034 | - /* 1380 */ 135, 99, 99, 99, 99, 99, 99, 99, 99, 99, | |
| 1035 | - /* 1390 */ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, | |
| 1036 | - /* 1400 */ 99, 99, | |
| 912 | + /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, | |
| 913 | + /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, | |
| 914 | + /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, | |
| 915 | + /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, | |
| 916 | + /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, | |
| 917 | + /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, | |
| 918 | + /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, | |
| 919 | + /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, | |
| 920 | + /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, | |
| 921 | + /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, | |
| 922 | + /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, | |
| 923 | + /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, | |
| 924 | + /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, | |
| 925 | + /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, | |
| 926 | + /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, | |
| 927 | + /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, | |
| 928 | + /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, | |
| 929 | + /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, | |
| 930 | + /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, | |
| 931 | + /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, | |
| 932 | + /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, | |
| 933 | + /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, | |
| 934 | + /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, | |
| 935 | + /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, | |
| 936 | + /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, | |
| 937 | + /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, | |
| 938 | + /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, | |
| 939 | + /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, | |
| 940 | + /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, | |
| 941 | + /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, | |
| 942 | + /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, | |
| 943 | + /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, | |
| 944 | + /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, | |
| 945 | + /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, | |
| 946 | + /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, | |
| 947 | + /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, | |
| 948 | + /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, | |
| 949 | + /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, | |
| 950 | + /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, | |
| 951 | + /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, | |
| 952 | + /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, | |
| 953 | + /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, | |
| 954 | + /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, | |
| 955 | + /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, | |
| 956 | + /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, | |
| 957 | + /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, | |
| 958 | + /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, | |
| 959 | + /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, | |
| 960 | + /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, | |
| 961 | + /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, | |
| 962 | + /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, | |
| 963 | + /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, | |
| 964 | + /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, | |
| 965 | + /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, | |
| 966 | + /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, | |
| 967 | + /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, | |
| 968 | + /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, | |
| 969 | + /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, | |
| 970 | + /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, | |
| 971 | + /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, | |
| 972 | + /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, | |
| 973 | + /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, | |
| 974 | + /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, | |
| 975 | + /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, | |
| 976 | + /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, | |
| 977 | + /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, | |
| 978 | + /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, | |
| 979 | + /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, | |
| 980 | + /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, | |
| 981 | + /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, | |
| 982 | + /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, | |
| 983 | + /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, | |
| 984 | + /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, | |
| 985 | + /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, | |
| 986 | + /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, | |
| 987 | + /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, | |
| 988 | + /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, | |
| 989 | + /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, | |
| 990 | + /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, | |
| 991 | + /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, | |
| 992 | + /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, | |
| 993 | + /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, | |
| 994 | + /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, | |
| 995 | + /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, | |
| 996 | + /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, | |
| 997 | + /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, | |
| 998 | + /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, | |
| 999 | + /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, | |
| 1000 | + /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, | |
| 1001 | + /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, | |
| 1002 | + /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, | |
| 1003 | + /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, | |
| 1004 | + /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, | |
| 1005 | + /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, | |
| 1006 | + /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, | |
| 1007 | + /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, | |
| 1008 | + /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, | |
| 1009 | + /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, | |
| 1010 | + /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, | |
| 1011 | + /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, | |
| 1012 | + /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, | |
| 1013 | + /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, | |
| 1014 | + /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, | |
| 1015 | + /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, | |
| 1016 | + /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, | |
| 1017 | + /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, | |
| 1018 | + /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, | |
| 1019 | + /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, | |
| 1020 | + /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, | |
| 1021 | + /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, | |
| 1022 | + /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, | |
| 1023 | + /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, | |
| 1024 | + /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1025 | + /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, | |
| 1026 | + /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, | |
| 1027 | + /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1028 | + /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, | |
| 1029 | + /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1030 | + /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, | |
| 1031 | + /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1032 | + /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1033 | + /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1034 | + /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1035 | + /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1036 | + /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1037 | + /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1038 | + /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1039 | + /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1040 | + /* 1410 */ 100, 100, 100, | |
| 1037 | 1041 | }; |
| 1038 | 1042 | #define YY_SHIFT_COUNT (163) |
| 1039 | 1043 | #define YY_SHIFT_MIN (0) |
| 1040 | -#define YY_SHIFT_MAX (1277) | |
| 1044 | +#define YY_SHIFT_MAX (1113) | |
| 1041 | 1045 | static const unsigned short int yy_shift_ofst[] = { |
| 1042 | - /* 0 */ 143, 127, 221, 244, 244, 244, 244, 244, 244, 244, | |
| 1043 | - /* 10 */ 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, | |
| 1044 | - /* 20 */ 244, 244, 244, 244, 244, 244, 244, 276, 510, 557, | |
| 1045 | - /* 30 */ 276, 143, 347, 347, 0, 64, 143, 573, 557, 573, | |
| 1046 | - /* 40 */ 400, 400, 400, 442, 539, 557, 557, 557, 557, 557, | |
| 1047 | - /* 50 */ 557, 604, 557, 557, 667, 557, 557, 557, 557, 557, | |
| 1048 | - /* 60 */ 557, 557, 557, 557, 557, 218, 60, 60, 60, 60, | |
| 1049 | - /* 70 */ 60, 145, 315, 393, 471, 292, 292, 170, 71, 1303, | |
| 1050 | - /* 80 */ 1303, 1303, 1303, 114, 114, 338, 402, 129, 444, 367, | |
| 1051 | - /* 90 */ 683, 589, 1251, 669, 1255, 1048, 1277, 1077, 1106, 25, | |
| 1052 | - /* 100 */ 25, 25, 184, 25, 25, 25, 168, 25, 429, 83, | |
| 1053 | - /* 110 */ 92, 105, 70, 133, 138, 182, 182, 234, 257, 137, | |
| 1054 | - /* 120 */ 149, 289, 141, 155, 151, 146, 156, 147, 174, 176, | |
| 1055 | - /* 130 */ 196, 203, 204, 179, 237, 249, 213, 261, 211, 214, | |
| 1056 | - /* 140 */ 215, 222, 290, 300, 307, 278, 323, 330, 336, 246, | |
| 1057 | - /* 150 */ 274, 329, 246, 343, 345, 346, 348, 351, 352, 353, | |
| 1058 | - /* 160 */ 372, 297, 384, 377, | |
| 1046 | + /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, | |
| 1047 | + /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, | |
| 1048 | + /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, | |
| 1049 | + /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, | |
| 1050 | + /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, | |
| 1051 | + /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, | |
| 1052 | + /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, | |
| 1053 | + /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, | |
| 1054 | + /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, | |
| 1055 | + /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, | |
| 1056 | + /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, | |
| 1057 | + /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, | |
| 1058 | + /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, | |
| 1059 | + /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, | |
| 1060 | + /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, | |
| 1061 | + /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, | |
| 1062 | + /* 160 */ 363, 293, 374, 373, | |
| 1059 | 1063 | }; |
| 1060 | 1064 | #define YY_REDUCE_COUNT (82) |
| 1061 | -#define YY_REDUCE_MIN (-129) | |
| 1062 | -#define YY_REDUCE_MAX (1139) | |
| 1065 | +#define YY_REDUCE_MIN (-130) | |
| 1066 | +#define YY_REDUCE_MAX (1177) | |
| 1063 | 1067 | static const short yy_reduce_ofst[] = { |
| 1064 | - /* 0 */ 363, -96, -32, 93, 152, 394, 512, 599, 616, 631, | |
| 1065 | - /* 10 */ 664, 680, 696, 710, 722, 735, 749, 761, 775, 791, | |
| 1066 | - /* 20 */ 807, 821, 833, 846, 860, 872, 886, 889, 668, 905, | |
| 1067 | - /* 30 */ 913, 464, 603, 779, -61, -61, 535, 650, 932, 945, | |
| 1068 | - /* 40 */ 794, 929, 957, 961, 964, 987, 990, 993, 1006, 1019, | |
| 1069 | - /* 50 */ 1025, 1032, 1038, 1044, 1061, 1067, 1074, 1079, 1091, 1097, | |
| 1070 | - /* 60 */ 1103, 1120, 1132, 1136, 1139, -81, -111, -101, -47, -37, | |
| 1071 | - /* 70 */ -23, -22, -129, -129, -129, -97, -86, -58, -100, -15, | |
| 1072 | - /* 80 */ 30, 34, 24, | |
| 1068 | + /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, | |
| 1069 | + /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, | |
| 1070 | + /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, | |
| 1071 | + /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, | |
| 1072 | + /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, | |
| 1073 | + /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, | |
| 1074 | + /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, | |
| 1075 | + /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, | |
| 1076 | + /* 80 */ 46, 52, -36, | |
| 1073 | 1077 | }; |
| 1074 | 1078 | static const YYACTIONTYPE yy_default[] = { |
| 1075 | 1079 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1076 | 1080 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1077 | 1081 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1177,10 +1181,11 @@ | ||
| 1177 | 1181 | 0, /* RJUST => nothing */ |
| 1178 | 1182 | 0, /* ABOVE => nothing */ |
| 1179 | 1183 | 0, /* BELOW => nothing */ |
| 1180 | 1184 | 0, /* ITALIC => nothing */ |
| 1181 | 1185 | 0, /* BOLD => nothing */ |
| 1186 | + 0, /* MONO => nothing */ | |
| 1182 | 1187 | 0, /* ALIGNED => nothing */ |
| 1183 | 1188 | 0, /* BIG => nothing */ |
| 1184 | 1189 | 0, /* SMALL => nothing */ |
| 1185 | 1190 | 0, /* AND => nothing */ |
| 1186 | 1191 | 0, /* LT => nothing */ |
| @@ -1364,74 +1369,75 @@ | ||
| 1364 | 1369 | /* 66 */ "RJUST", |
| 1365 | 1370 | /* 67 */ "ABOVE", |
| 1366 | 1371 | /* 68 */ "BELOW", |
| 1367 | 1372 | /* 69 */ "ITALIC", |
| 1368 | 1373 | /* 70 */ "BOLD", |
| 1369 | - /* 71 */ "ALIGNED", | |
| 1370 | - /* 72 */ "BIG", | |
| 1371 | - /* 73 */ "SMALL", | |
| 1372 | - /* 74 */ "AND", | |
| 1373 | - /* 75 */ "LT", | |
| 1374 | - /* 76 */ "GT", | |
| 1375 | - /* 77 */ "ON", | |
| 1376 | - /* 78 */ "WAY", | |
| 1377 | - /* 79 */ "BETWEEN", | |
| 1378 | - /* 80 */ "THE", | |
| 1379 | - /* 81 */ "NTH", | |
| 1380 | - /* 82 */ "VERTEX", | |
| 1381 | - /* 83 */ "TOP", | |
| 1382 | - /* 84 */ "BOTTOM", | |
| 1383 | - /* 85 */ "START", | |
| 1384 | - /* 86 */ "END", | |
| 1385 | - /* 87 */ "IN", | |
| 1386 | - /* 88 */ "THIS", | |
| 1387 | - /* 89 */ "DOT_U", | |
| 1388 | - /* 90 */ "LAST", | |
| 1389 | - /* 91 */ "NUMBER", | |
| 1390 | - /* 92 */ "FUNC1", | |
| 1391 | - /* 93 */ "FUNC2", | |
| 1392 | - /* 94 */ "DIST", | |
| 1393 | - /* 95 */ "DOT_XY", | |
| 1394 | - /* 96 */ "X", | |
| 1395 | - /* 97 */ "Y", | |
| 1396 | - /* 98 */ "DOT_L", | |
| 1397 | - /* 99 */ "statement_list", | |
| 1398 | - /* 100 */ "statement", | |
| 1399 | - /* 101 */ "unnamed_statement", | |
| 1400 | - /* 102 */ "basetype", | |
| 1401 | - /* 103 */ "expr", | |
| 1402 | - /* 104 */ "numproperty", | |
| 1403 | - /* 105 */ "edge", | |
| 1404 | - /* 106 */ "direction", | |
| 1405 | - /* 107 */ "dashproperty", | |
| 1406 | - /* 108 */ "colorproperty", | |
| 1407 | - /* 109 */ "locproperty", | |
| 1408 | - /* 110 */ "position", | |
| 1409 | - /* 111 */ "place", | |
| 1410 | - /* 112 */ "object", | |
| 1411 | - /* 113 */ "objectname", | |
| 1412 | - /* 114 */ "nth", | |
| 1413 | - /* 115 */ "textposition", | |
| 1414 | - /* 116 */ "rvalue", | |
| 1415 | - /* 117 */ "lvalue", | |
| 1416 | - /* 118 */ "even", | |
| 1417 | - /* 119 */ "relexpr", | |
| 1418 | - /* 120 */ "optrelexpr", | |
| 1419 | - /* 121 */ "document", | |
| 1420 | - /* 122 */ "print", | |
| 1421 | - /* 123 */ "prlist", | |
| 1422 | - /* 124 */ "pritem", | |
| 1423 | - /* 125 */ "prsep", | |
| 1424 | - /* 126 */ "attribute_list", | |
| 1425 | - /* 127 */ "savelist", | |
| 1426 | - /* 128 */ "alist", | |
| 1427 | - /* 129 */ "attribute", | |
| 1428 | - /* 130 */ "go", | |
| 1429 | - /* 131 */ "boolproperty", | |
| 1430 | - /* 132 */ "withclause", | |
| 1431 | - /* 133 */ "between", | |
| 1432 | - /* 134 */ "place2", | |
| 1374 | + /* 71 */ "MONO", | |
| 1375 | + /* 72 */ "ALIGNED", | |
| 1376 | + /* 73 */ "BIG", | |
| 1377 | + /* 74 */ "SMALL", | |
| 1378 | + /* 75 */ "AND", | |
| 1379 | + /* 76 */ "LT", | |
| 1380 | + /* 77 */ "GT", | |
| 1381 | + /* 78 */ "ON", | |
| 1382 | + /* 79 */ "WAY", | |
| 1383 | + /* 80 */ "BETWEEN", | |
| 1384 | + /* 81 */ "THE", | |
| 1385 | + /* 82 */ "NTH", | |
| 1386 | + /* 83 */ "VERTEX", | |
| 1387 | + /* 84 */ "TOP", | |
| 1388 | + /* 85 */ "BOTTOM", | |
| 1389 | + /* 86 */ "START", | |
| 1390 | + /* 87 */ "END", | |
| 1391 | + /* 88 */ "IN", | |
| 1392 | + /* 89 */ "THIS", | |
| 1393 | + /* 90 */ "DOT_U", | |
| 1394 | + /* 91 */ "LAST", | |
| 1395 | + /* 92 */ "NUMBER", | |
| 1396 | + /* 93 */ "FUNC1", | |
| 1397 | + /* 94 */ "FUNC2", | |
| 1398 | + /* 95 */ "DIST", | |
| 1399 | + /* 96 */ "DOT_XY", | |
| 1400 | + /* 97 */ "X", | |
| 1401 | + /* 98 */ "Y", | |
| 1402 | + /* 99 */ "DOT_L", | |
| 1403 | + /* 100 */ "statement_list", | |
| 1404 | + /* 101 */ "statement", | |
| 1405 | + /* 102 */ "unnamed_statement", | |
| 1406 | + /* 103 */ "basetype", | |
| 1407 | + /* 104 */ "expr", | |
| 1408 | + /* 105 */ "numproperty", | |
| 1409 | + /* 106 */ "edge", | |
| 1410 | + /* 107 */ "direction", | |
| 1411 | + /* 108 */ "dashproperty", | |
| 1412 | + /* 109 */ "colorproperty", | |
| 1413 | + /* 110 */ "locproperty", | |
| 1414 | + /* 111 */ "position", | |
| 1415 | + /* 112 */ "place", | |
| 1416 | + /* 113 */ "object", | |
| 1417 | + /* 114 */ "objectname", | |
| 1418 | + /* 115 */ "nth", | |
| 1419 | + /* 116 */ "textposition", | |
| 1420 | + /* 117 */ "rvalue", | |
| 1421 | + /* 118 */ "lvalue", | |
| 1422 | + /* 119 */ "even", | |
| 1423 | + /* 120 */ "relexpr", | |
| 1424 | + /* 121 */ "optrelexpr", | |
| 1425 | + /* 122 */ "document", | |
| 1426 | + /* 123 */ "print", | |
| 1427 | + /* 124 */ "prlist", | |
| 1428 | + /* 125 */ "pritem", | |
| 1429 | + /* 126 */ "prsep", | |
| 1430 | + /* 127 */ "attribute_list", | |
| 1431 | + /* 128 */ "savelist", | |
| 1432 | + /* 129 */ "alist", | |
| 1433 | + /* 130 */ "attribute", | |
| 1434 | + /* 131 */ "go", | |
| 1435 | + /* 132 */ "boolproperty", | |
| 1436 | + /* 133 */ "withclause", | |
| 1437 | + /* 134 */ "between", | |
| 1438 | + /* 135 */ "place2", | |
| 1433 | 1439 | }; |
| 1434 | 1440 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1435 | 1441 | |
| 1436 | 1442 | #ifndef NDEBUG |
| 1437 | 1443 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1498,11 +1504,11 @@ | ||
| 1498 | 1504 | /* 58 */ "boolproperty ::= INVIS", |
| 1499 | 1505 | /* 59 */ "boolproperty ::= THICK", |
| 1500 | 1506 | /* 60 */ "boolproperty ::= THIN", |
| 1501 | 1507 | /* 61 */ "boolproperty ::= SOLID", |
| 1502 | 1508 | /* 62 */ "textposition ::=", |
| 1503 | - /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL", | |
| 1509 | + /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", | |
| 1504 | 1510 | /* 64 */ "position ::= expr COMMA expr", |
| 1505 | 1511 | /* 65 */ "position ::= place PLUS expr COMMA expr", |
| 1506 | 1512 | /* 66 */ "position ::= place MINUS expr COMMA expr", |
| 1507 | 1513 | /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", |
| 1508 | 1514 | /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", |
| @@ -1716,24 +1722,24 @@ | ||
| 1716 | 1722 | ** Note: during a reduce, the only symbols destroyed are those |
| 1717 | 1723 | ** which appear on the RHS of the rule, but which are *not* used |
| 1718 | 1724 | ** inside the C code. |
| 1719 | 1725 | */ |
| 1720 | 1726 | /********* Begin destructor definitions ***************************************/ |
| 1721 | - case 99: /* statement_list */ | |
| 1722 | -{ | |
| 1723 | -#line 509 "pikchr.y" | |
| 1724 | -pik_elist_free(p,(yypminor->yy227)); | |
| 1725 | -#line 1750 "pikchr.c" | |
| 1726 | -} | |
| 1727 | - break; | |
| 1728 | - case 100: /* statement */ | |
| 1729 | - case 101: /* unnamed_statement */ | |
| 1730 | - case 102: /* basetype */ | |
| 1731 | -{ | |
| 1732 | -#line 511 "pikchr.y" | |
| 1733 | -pik_elem_free(p,(yypminor->yy36)); | |
| 1734 | -#line 1759 "pikchr.c" | |
| 1727 | + case 100: /* statement_list */ | |
| 1728 | +{ | |
| 1729 | +#line 510 "pikchr.y" | |
| 1730 | +pik_elist_free(p,(yypminor->yy235)); | |
| 1731 | +#line 1756 "pikchr.c" | |
| 1732 | +} | |
| 1733 | + break; | |
| 1734 | + case 101: /* statement */ | |
| 1735 | + case 102: /* unnamed_statement */ | |
| 1736 | + case 103: /* basetype */ | |
| 1737 | +{ | |
| 1738 | +#line 512 "pikchr.y" | |
| 1739 | +pik_elem_free(p,(yypminor->yy162)); | |
| 1740 | +#line 1765 "pikchr.c" | |
| 1735 | 1741 | } |
| 1736 | 1742 | break; |
| 1737 | 1743 | /********* End destructor definitions *****************************************/ |
| 1738 | 1744 | default: break; /* If no destructor action specified: do nothing */ |
| 1739 | 1745 | } |
| @@ -1947,14 +1953,14 @@ | ||
| 1947 | 1953 | #endif |
| 1948 | 1954 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 1949 | 1955 | /* Here code is inserted which will execute if the parser |
| 1950 | 1956 | ** stack every overflows */ |
| 1951 | 1957 | /******** Begin %stack_overflow code ******************************************/ |
| 1952 | -#line 543 "pikchr.y" | |
| 1958 | +#line 544 "pikchr.y" | |
| 1953 | 1959 | |
| 1954 | 1960 | pik_error(p, 0, "parser stack overflow"); |
| 1955 | -#line 1980 "pikchr.c" | |
| 1961 | +#line 1986 "pikchr.c" | |
| 1956 | 1962 | /******** End %stack_overflow code ********************************************/ |
| 1957 | 1963 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 1958 | 1964 | pik_parserCTX_STORE |
| 1959 | 1965 | } |
| 1960 | 1966 | |
| @@ -2022,166 +2028,166 @@ | ||
| 2022 | 2028 | } |
| 2023 | 2029 | |
| 2024 | 2030 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2025 | 2031 | ** of that rule */ |
| 2026 | 2032 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2027 | - 121, /* (0) document ::= statement_list */ | |
| 2028 | - 99, /* (1) statement_list ::= statement */ | |
| 2029 | - 99, /* (2) statement_list ::= statement_list EOL statement */ | |
| 2030 | - 100, /* (3) statement ::= */ | |
| 2031 | - 100, /* (4) statement ::= direction */ | |
| 2032 | - 100, /* (5) statement ::= lvalue ASSIGN rvalue */ | |
| 2033 | - 100, /* (6) statement ::= PLACENAME COLON unnamed_statement */ | |
| 2034 | - 100, /* (7) statement ::= PLACENAME COLON position */ | |
| 2035 | - 100, /* (8) statement ::= unnamed_statement */ | |
| 2036 | - 100, /* (9) statement ::= print prlist */ | |
| 2037 | - 100, /* (10) statement ::= ASSERT LP expr EQ expr RP */ | |
| 2038 | - 100, /* (11) statement ::= ASSERT LP position EQ position RP */ | |
| 2039 | - 100, /* (12) statement ::= DEFINE ID CODEBLOCK */ | |
| 2040 | - 116, /* (13) rvalue ::= PLACENAME */ | |
| 2041 | - 124, /* (14) pritem ::= FILL */ | |
| 2042 | - 124, /* (15) pritem ::= COLOR */ | |
| 2043 | - 124, /* (16) pritem ::= THICKNESS */ | |
| 2044 | - 124, /* (17) pritem ::= rvalue */ | |
| 2045 | - 124, /* (18) pritem ::= STRING */ | |
| 2046 | - 125, /* (19) prsep ::= COMMA */ | |
| 2047 | - 101, /* (20) unnamed_statement ::= basetype attribute_list */ | |
| 2048 | - 102, /* (21) basetype ::= CLASSNAME */ | |
| 2049 | - 102, /* (22) basetype ::= STRING textposition */ | |
| 2050 | - 102, /* (23) basetype ::= LB savelist statement_list RB */ | |
| 2051 | - 127, /* (24) savelist ::= */ | |
| 2052 | - 119, /* (25) relexpr ::= expr */ | |
| 2053 | - 119, /* (26) relexpr ::= expr PERCENT */ | |
| 2054 | - 120, /* (27) optrelexpr ::= */ | |
| 2055 | - 126, /* (28) attribute_list ::= relexpr alist */ | |
| 2056 | - 129, /* (29) attribute ::= numproperty relexpr */ | |
| 2057 | - 129, /* (30) attribute ::= dashproperty expr */ | |
| 2058 | - 129, /* (31) attribute ::= dashproperty */ | |
| 2059 | - 129, /* (32) attribute ::= colorproperty rvalue */ | |
| 2060 | - 129, /* (33) attribute ::= go direction optrelexpr */ | |
| 2061 | - 129, /* (34) attribute ::= go direction even position */ | |
| 2062 | - 129, /* (35) attribute ::= CLOSE */ | |
| 2063 | - 129, /* (36) attribute ::= CHOP */ | |
| 2064 | - 129, /* (37) attribute ::= FROM position */ | |
| 2065 | - 129, /* (38) attribute ::= TO position */ | |
| 2066 | - 129, /* (39) attribute ::= THEN */ | |
| 2067 | - 129, /* (40) attribute ::= THEN optrelexpr HEADING expr */ | |
| 2068 | - 129, /* (41) attribute ::= THEN optrelexpr EDGEPT */ | |
| 2069 | - 129, /* (42) attribute ::= GO optrelexpr HEADING expr */ | |
| 2070 | - 129, /* (43) attribute ::= GO optrelexpr EDGEPT */ | |
| 2071 | - 129, /* (44) attribute ::= AT position */ | |
| 2072 | - 129, /* (45) attribute ::= SAME */ | |
| 2073 | - 129, /* (46) attribute ::= SAME AS object */ | |
| 2074 | - 129, /* (47) attribute ::= STRING textposition */ | |
| 2075 | - 129, /* (48) attribute ::= FIT */ | |
| 2076 | - 129, /* (49) attribute ::= BEHIND object */ | |
| 2077 | - 132, /* (50) withclause ::= DOT_E edge AT position */ | |
| 2078 | - 132, /* (51) withclause ::= edge AT position */ | |
| 2079 | - 104, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ | |
| 2080 | - 131, /* (53) boolproperty ::= CW */ | |
| 2081 | - 131, /* (54) boolproperty ::= CCW */ | |
| 2082 | - 131, /* (55) boolproperty ::= LARROW */ | |
| 2083 | - 131, /* (56) boolproperty ::= RARROW */ | |
| 2084 | - 131, /* (57) boolproperty ::= LRARROW */ | |
| 2085 | - 131, /* (58) boolproperty ::= INVIS */ | |
| 2086 | - 131, /* (59) boolproperty ::= THICK */ | |
| 2087 | - 131, /* (60) boolproperty ::= THIN */ | |
| 2088 | - 131, /* (61) boolproperty ::= SOLID */ | |
| 2089 | - 115, /* (62) textposition ::= */ | |
| 2090 | - 115, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ | |
| 2091 | - 110, /* (64) position ::= expr COMMA expr */ | |
| 2092 | - 110, /* (65) position ::= place PLUS expr COMMA expr */ | |
| 2093 | - 110, /* (66) position ::= place MINUS expr COMMA expr */ | |
| 2094 | - 110, /* (67) position ::= place PLUS LP expr COMMA expr RP */ | |
| 2095 | - 110, /* (68) position ::= place MINUS LP expr COMMA expr RP */ | |
| 2096 | - 110, /* (69) position ::= LP position COMMA position RP */ | |
| 2097 | - 110, /* (70) position ::= LP position RP */ | |
| 2098 | - 110, /* (71) position ::= expr between position AND position */ | |
| 2099 | - 110, /* (72) position ::= expr LT position COMMA position GT */ | |
| 2100 | - 110, /* (73) position ::= expr ABOVE position */ | |
| 2101 | - 110, /* (74) position ::= expr BELOW position */ | |
| 2102 | - 110, /* (75) position ::= expr LEFT OF position */ | |
| 2103 | - 110, /* (76) position ::= expr RIGHT OF position */ | |
| 2104 | - 110, /* (77) position ::= expr ON HEADING EDGEPT OF position */ | |
| 2105 | - 110, /* (78) position ::= expr HEADING EDGEPT OF position */ | |
| 2106 | - 110, /* (79) position ::= expr EDGEPT OF position */ | |
| 2107 | - 110, /* (80) position ::= expr ON HEADING expr FROM position */ | |
| 2108 | - 110, /* (81) position ::= expr HEADING expr FROM position */ | |
| 2109 | - 111, /* (82) place ::= edge OF object */ | |
| 2110 | - 134, /* (83) place2 ::= object */ | |
| 2111 | - 134, /* (84) place2 ::= object DOT_E edge */ | |
| 2112 | - 134, /* (85) place2 ::= NTH VERTEX OF object */ | |
| 2113 | - 112, /* (86) object ::= nth */ | |
| 2114 | - 112, /* (87) object ::= nth OF|IN object */ | |
| 2115 | - 113, /* (88) objectname ::= THIS */ | |
| 2116 | - 113, /* (89) objectname ::= PLACENAME */ | |
| 2117 | - 113, /* (90) objectname ::= objectname DOT_U PLACENAME */ | |
| 2118 | - 114, /* (91) nth ::= NTH CLASSNAME */ | |
| 2119 | - 114, /* (92) nth ::= NTH LAST CLASSNAME */ | |
| 2120 | - 114, /* (93) nth ::= LAST CLASSNAME */ | |
| 2121 | - 114, /* (94) nth ::= LAST */ | |
| 2122 | - 114, /* (95) nth ::= NTH LB RB */ | |
| 2123 | - 114, /* (96) nth ::= NTH LAST LB RB */ | |
| 2124 | - 114, /* (97) nth ::= LAST LB RB */ | |
| 2125 | - 103, /* (98) expr ::= expr PLUS expr */ | |
| 2126 | - 103, /* (99) expr ::= expr MINUS expr */ | |
| 2127 | - 103, /* (100) expr ::= expr STAR expr */ | |
| 2128 | - 103, /* (101) expr ::= expr SLASH expr */ | |
| 2129 | - 103, /* (102) expr ::= MINUS expr */ | |
| 2130 | - 103, /* (103) expr ::= PLUS expr */ | |
| 2131 | - 103, /* (104) expr ::= LP expr RP */ | |
| 2132 | - 103, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ | |
| 2133 | - 103, /* (106) expr ::= NUMBER */ | |
| 2134 | - 103, /* (107) expr ::= ID */ | |
| 2135 | - 103, /* (108) expr ::= FUNC1 LP expr RP */ | |
| 2136 | - 103, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ | |
| 2137 | - 103, /* (110) expr ::= DIST LP position COMMA position RP */ | |
| 2138 | - 103, /* (111) expr ::= place2 DOT_XY X */ | |
| 2139 | - 103, /* (112) expr ::= place2 DOT_XY Y */ | |
| 2140 | - 103, /* (113) expr ::= object DOT_L numproperty */ | |
| 2141 | - 103, /* (114) expr ::= object DOT_L dashproperty */ | |
| 2142 | - 103, /* (115) expr ::= object DOT_L colorproperty */ | |
| 2143 | - 117, /* (116) lvalue ::= ID */ | |
| 2144 | - 117, /* (117) lvalue ::= FILL */ | |
| 2145 | - 117, /* (118) lvalue ::= COLOR */ | |
| 2146 | - 117, /* (119) lvalue ::= THICKNESS */ | |
| 2147 | - 116, /* (120) rvalue ::= expr */ | |
| 2148 | - 122, /* (121) print ::= PRINT */ | |
| 2149 | - 123, /* (122) prlist ::= pritem */ | |
| 2150 | - 123, /* (123) prlist ::= prlist prsep pritem */ | |
| 2151 | - 106, /* (124) direction ::= UP */ | |
| 2152 | - 106, /* (125) direction ::= DOWN */ | |
| 2153 | - 106, /* (126) direction ::= LEFT */ | |
| 2154 | - 106, /* (127) direction ::= RIGHT */ | |
| 2155 | - 120, /* (128) optrelexpr ::= relexpr */ | |
| 2156 | - 126, /* (129) attribute_list ::= alist */ | |
| 2157 | - 128, /* (130) alist ::= */ | |
| 2158 | - 128, /* (131) alist ::= alist attribute */ | |
| 2159 | - 129, /* (132) attribute ::= boolproperty */ | |
| 2160 | - 129, /* (133) attribute ::= WITH withclause */ | |
| 2161 | - 130, /* (134) go ::= GO */ | |
| 2162 | - 130, /* (135) go ::= */ | |
| 2163 | - 118, /* (136) even ::= UNTIL EVEN WITH */ | |
| 2164 | - 118, /* (137) even ::= EVEN WITH */ | |
| 2165 | - 107, /* (138) dashproperty ::= DOTTED */ | |
| 2166 | - 107, /* (139) dashproperty ::= DASHED */ | |
| 2167 | - 108, /* (140) colorproperty ::= FILL */ | |
| 2168 | - 108, /* (141) colorproperty ::= COLOR */ | |
| 2169 | - 110, /* (142) position ::= place */ | |
| 2170 | - 133, /* (143) between ::= WAY BETWEEN */ | |
| 2171 | - 133, /* (144) between ::= BETWEEN */ | |
| 2172 | - 133, /* (145) between ::= OF THE WAY BETWEEN */ | |
| 2173 | - 111, /* (146) place ::= place2 */ | |
| 2174 | - 105, /* (147) edge ::= CENTER */ | |
| 2175 | - 105, /* (148) edge ::= EDGEPT */ | |
| 2176 | - 105, /* (149) edge ::= TOP */ | |
| 2177 | - 105, /* (150) edge ::= BOTTOM */ | |
| 2178 | - 105, /* (151) edge ::= START */ | |
| 2179 | - 105, /* (152) edge ::= END */ | |
| 2180 | - 105, /* (153) edge ::= RIGHT */ | |
| 2181 | - 105, /* (154) edge ::= LEFT */ | |
| 2182 | - 112, /* (155) object ::= objectname */ | |
| 2033 | + 122, /* (0) document ::= statement_list */ | |
| 2034 | + 100, /* (1) statement_list ::= statement */ | |
| 2035 | + 100, /* (2) statement_list ::= statement_list EOL statement */ | |
| 2036 | + 101, /* (3) statement ::= */ | |
| 2037 | + 101, /* (4) statement ::= direction */ | |
| 2038 | + 101, /* (5) statement ::= lvalue ASSIGN rvalue */ | |
| 2039 | + 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ | |
| 2040 | + 101, /* (7) statement ::= PLACENAME COLON position */ | |
| 2041 | + 101, /* (8) statement ::= unnamed_statement */ | |
| 2042 | + 101, /* (9) statement ::= print prlist */ | |
| 2043 | + 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ | |
| 2044 | + 101, /* (11) statement ::= ASSERT LP position EQ position RP */ | |
| 2045 | + 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ | |
| 2046 | + 117, /* (13) rvalue ::= PLACENAME */ | |
| 2047 | + 125, /* (14) pritem ::= FILL */ | |
| 2048 | + 125, /* (15) pritem ::= COLOR */ | |
| 2049 | + 125, /* (16) pritem ::= THICKNESS */ | |
| 2050 | + 125, /* (17) pritem ::= rvalue */ | |
| 2051 | + 125, /* (18) pritem ::= STRING */ | |
| 2052 | + 126, /* (19) prsep ::= COMMA */ | |
| 2053 | + 102, /* (20) unnamed_statement ::= basetype attribute_list */ | |
| 2054 | + 103, /* (21) basetype ::= CLASSNAME */ | |
| 2055 | + 103, /* (22) basetype ::= STRING textposition */ | |
| 2056 | + 103, /* (23) basetype ::= LB savelist statement_list RB */ | |
| 2057 | + 128, /* (24) savelist ::= */ | |
| 2058 | + 120, /* (25) relexpr ::= expr */ | |
| 2059 | + 120, /* (26) relexpr ::= expr PERCENT */ | |
| 2060 | + 121, /* (27) optrelexpr ::= */ | |
| 2061 | + 127, /* (28) attribute_list ::= relexpr alist */ | |
| 2062 | + 130, /* (29) attribute ::= numproperty relexpr */ | |
| 2063 | + 130, /* (30) attribute ::= dashproperty expr */ | |
| 2064 | + 130, /* (31) attribute ::= dashproperty */ | |
| 2065 | + 130, /* (32) attribute ::= colorproperty rvalue */ | |
| 2066 | + 130, /* (33) attribute ::= go direction optrelexpr */ | |
| 2067 | + 130, /* (34) attribute ::= go direction even position */ | |
| 2068 | + 130, /* (35) attribute ::= CLOSE */ | |
| 2069 | + 130, /* (36) attribute ::= CHOP */ | |
| 2070 | + 130, /* (37) attribute ::= FROM position */ | |
| 2071 | + 130, /* (38) attribute ::= TO position */ | |
| 2072 | + 130, /* (39) attribute ::= THEN */ | |
| 2073 | + 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ | |
| 2074 | + 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ | |
| 2075 | + 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ | |
| 2076 | + 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ | |
| 2077 | + 130, /* (44) attribute ::= AT position */ | |
| 2078 | + 130, /* (45) attribute ::= SAME */ | |
| 2079 | + 130, /* (46) attribute ::= SAME AS object */ | |
| 2080 | + 130, /* (47) attribute ::= STRING textposition */ | |
| 2081 | + 130, /* (48) attribute ::= FIT */ | |
| 2082 | + 130, /* (49) attribute ::= BEHIND object */ | |
| 2083 | + 133, /* (50) withclause ::= DOT_E edge AT position */ | |
| 2084 | + 133, /* (51) withclause ::= edge AT position */ | |
| 2085 | + 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ | |
| 2086 | + 132, /* (53) boolproperty ::= CW */ | |
| 2087 | + 132, /* (54) boolproperty ::= CCW */ | |
| 2088 | + 132, /* (55) boolproperty ::= LARROW */ | |
| 2089 | + 132, /* (56) boolproperty ::= RARROW */ | |
| 2090 | + 132, /* (57) boolproperty ::= LRARROW */ | |
| 2091 | + 132, /* (58) boolproperty ::= INVIS */ | |
| 2092 | + 132, /* (59) boolproperty ::= THICK */ | |
| 2093 | + 132, /* (60) boolproperty ::= THIN */ | |
| 2094 | + 132, /* (61) boolproperty ::= SOLID */ | |
| 2095 | + 116, /* (62) textposition ::= */ | |
| 2096 | + 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ | |
| 2097 | + 111, /* (64) position ::= expr COMMA expr */ | |
| 2098 | + 111, /* (65) position ::= place PLUS expr COMMA expr */ | |
| 2099 | + 111, /* (66) position ::= place MINUS expr COMMA expr */ | |
| 2100 | + 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ | |
| 2101 | + 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ | |
| 2102 | + 111, /* (69) position ::= LP position COMMA position RP */ | |
| 2103 | + 111, /* (70) position ::= LP position RP */ | |
| 2104 | + 111, /* (71) position ::= expr between position AND position */ | |
| 2105 | + 111, /* (72) position ::= expr LT position COMMA position GT */ | |
| 2106 | + 111, /* (73) position ::= expr ABOVE position */ | |
| 2107 | + 111, /* (74) position ::= expr BELOW position */ | |
| 2108 | + 111, /* (75) position ::= expr LEFT OF position */ | |
| 2109 | + 111, /* (76) position ::= expr RIGHT OF position */ | |
| 2110 | + 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ | |
| 2111 | + 111, /* (78) position ::= expr HEADING EDGEPT OF position */ | |
| 2112 | + 111, /* (79) position ::= expr EDGEPT OF position */ | |
| 2113 | + 111, /* (80) position ::= expr ON HEADING expr FROM position */ | |
| 2114 | + 111, /* (81) position ::= expr HEADING expr FROM position */ | |
| 2115 | + 112, /* (82) place ::= edge OF object */ | |
| 2116 | + 135, /* (83) place2 ::= object */ | |
| 2117 | + 135, /* (84) place2 ::= object DOT_E edge */ | |
| 2118 | + 135, /* (85) place2 ::= NTH VERTEX OF object */ | |
| 2119 | + 113, /* (86) object ::= nth */ | |
| 2120 | + 113, /* (87) object ::= nth OF|IN object */ | |
| 2121 | + 114, /* (88) objectname ::= THIS */ | |
| 2122 | + 114, /* (89) objectname ::= PLACENAME */ | |
| 2123 | + 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ | |
| 2124 | + 115, /* (91) nth ::= NTH CLASSNAME */ | |
| 2125 | + 115, /* (92) nth ::= NTH LAST CLASSNAME */ | |
| 2126 | + 115, /* (93) nth ::= LAST CLASSNAME */ | |
| 2127 | + 115, /* (94) nth ::= LAST */ | |
| 2128 | + 115, /* (95) nth ::= NTH LB RB */ | |
| 2129 | + 115, /* (96) nth ::= NTH LAST LB RB */ | |
| 2130 | + 115, /* (97) nth ::= LAST LB RB */ | |
| 2131 | + 104, /* (98) expr ::= expr PLUS expr */ | |
| 2132 | + 104, /* (99) expr ::= expr MINUS expr */ | |
| 2133 | + 104, /* (100) expr ::= expr STAR expr */ | |
| 2134 | + 104, /* (101) expr ::= expr SLASH expr */ | |
| 2135 | + 104, /* (102) expr ::= MINUS expr */ | |
| 2136 | + 104, /* (103) expr ::= PLUS expr */ | |
| 2137 | + 104, /* (104) expr ::= LP expr RP */ | |
| 2138 | + 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ | |
| 2139 | + 104, /* (106) expr ::= NUMBER */ | |
| 2140 | + 104, /* (107) expr ::= ID */ | |
| 2141 | + 104, /* (108) expr ::= FUNC1 LP expr RP */ | |
| 2142 | + 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ | |
| 2143 | + 104, /* (110) expr ::= DIST LP position COMMA position RP */ | |
| 2144 | + 104, /* (111) expr ::= place2 DOT_XY X */ | |
| 2145 | + 104, /* (112) expr ::= place2 DOT_XY Y */ | |
| 2146 | + 104, /* (113) expr ::= object DOT_L numproperty */ | |
| 2147 | + 104, /* (114) expr ::= object DOT_L dashproperty */ | |
| 2148 | + 104, /* (115) expr ::= object DOT_L colorproperty */ | |
| 2149 | + 118, /* (116) lvalue ::= ID */ | |
| 2150 | + 118, /* (117) lvalue ::= FILL */ | |
| 2151 | + 118, /* (118) lvalue ::= COLOR */ | |
| 2152 | + 118, /* (119) lvalue ::= THICKNESS */ | |
| 2153 | + 117, /* (120) rvalue ::= expr */ | |
| 2154 | + 123, /* (121) print ::= PRINT */ | |
| 2155 | + 124, /* (122) prlist ::= pritem */ | |
| 2156 | + 124, /* (123) prlist ::= prlist prsep pritem */ | |
| 2157 | + 107, /* (124) direction ::= UP */ | |
| 2158 | + 107, /* (125) direction ::= DOWN */ | |
| 2159 | + 107, /* (126) direction ::= LEFT */ | |
| 2160 | + 107, /* (127) direction ::= RIGHT */ | |
| 2161 | + 121, /* (128) optrelexpr ::= relexpr */ | |
| 2162 | + 127, /* (129) attribute_list ::= alist */ | |
| 2163 | + 129, /* (130) alist ::= */ | |
| 2164 | + 129, /* (131) alist ::= alist attribute */ | |
| 2165 | + 130, /* (132) attribute ::= boolproperty */ | |
| 2166 | + 130, /* (133) attribute ::= WITH withclause */ | |
| 2167 | + 131, /* (134) go ::= GO */ | |
| 2168 | + 131, /* (135) go ::= */ | |
| 2169 | + 119, /* (136) even ::= UNTIL EVEN WITH */ | |
| 2170 | + 119, /* (137) even ::= EVEN WITH */ | |
| 2171 | + 108, /* (138) dashproperty ::= DOTTED */ | |
| 2172 | + 108, /* (139) dashproperty ::= DASHED */ | |
| 2173 | + 109, /* (140) colorproperty ::= FILL */ | |
| 2174 | + 109, /* (141) colorproperty ::= COLOR */ | |
| 2175 | + 111, /* (142) position ::= place */ | |
| 2176 | + 134, /* (143) between ::= WAY BETWEEN */ | |
| 2177 | + 134, /* (144) between ::= BETWEEN */ | |
| 2178 | + 134, /* (145) between ::= OF THE WAY BETWEEN */ | |
| 2179 | + 112, /* (146) place ::= place2 */ | |
| 2180 | + 106, /* (147) edge ::= CENTER */ | |
| 2181 | + 106, /* (148) edge ::= EDGEPT */ | |
| 2182 | + 106, /* (149) edge ::= TOP */ | |
| 2183 | + 106, /* (150) edge ::= BOTTOM */ | |
| 2184 | + 106, /* (151) edge ::= START */ | |
| 2185 | + 106, /* (152) edge ::= END */ | |
| 2186 | + 106, /* (153) edge ::= RIGHT */ | |
| 2187 | + 106, /* (154) edge ::= LEFT */ | |
| 2188 | + 113, /* (155) object ::= objectname */ | |
| 2183 | 2189 | }; |
| 2184 | 2190 | |
| 2185 | 2191 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2186 | 2192 | ** of symbols on the right-hand side of that rule. */ |
| 2187 | 2193 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2246,11 +2252,11 @@ | ||
| 2246 | 2252 | -1, /* (58) boolproperty ::= INVIS */ |
| 2247 | 2253 | -1, /* (59) boolproperty ::= THICK */ |
| 2248 | 2254 | -1, /* (60) boolproperty ::= THIN */ |
| 2249 | 2255 | -1, /* (61) boolproperty ::= SOLID */ |
| 2250 | 2256 | 0, /* (62) textposition ::= */ |
| 2251 | - -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ | |
| 2257 | + -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ | |
| 2252 | 2258 | -3, /* (64) position ::= expr COMMA expr */ |
| 2253 | 2259 | -5, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2254 | 2260 | -5, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2255 | 2261 | -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2256 | 2262 | -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| @@ -2381,620 +2387,620 @@ | ||
| 2381 | 2387 | ** break; |
| 2382 | 2388 | */ |
| 2383 | 2389 | /********** Begin reduce actions **********************************************/ |
| 2384 | 2390 | YYMINORTYPE yylhsminor; |
| 2385 | 2391 | case 0: /* document ::= statement_list */ |
| 2386 | -#line 547 "pikchr.y" | |
| 2387 | -{pik_render(p,yymsp[0].minor.yy227);} | |
| 2388 | -#line 2413 "pikchr.c" | |
| 2392 | +#line 548 "pikchr.y" | |
| 2393 | +{pik_render(p,yymsp[0].minor.yy235);} | |
| 2394 | +#line 2419 "pikchr.c" | |
| 2389 | 2395 | break; |
| 2390 | 2396 | case 1: /* statement_list ::= statement */ |
| 2391 | -#line 550 "pikchr.y" | |
| 2392 | -{ yylhsminor.yy227 = pik_elist_append(p,0,yymsp[0].minor.yy36); } | |
| 2393 | -#line 2418 "pikchr.c" | |
| 2394 | - yymsp[0].minor.yy227 = yylhsminor.yy227; | |
| 2397 | +#line 551 "pikchr.y" | |
| 2398 | +{ yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } | |
| 2399 | +#line 2424 "pikchr.c" | |
| 2400 | + yymsp[0].minor.yy235 = yylhsminor.yy235; | |
| 2395 | 2401 | break; |
| 2396 | 2402 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2397 | -#line 552 "pikchr.y" | |
| 2398 | -{ yylhsminor.yy227 = pik_elist_append(p,yymsp[-2].minor.yy227,yymsp[0].minor.yy36); } | |
| 2399 | -#line 2424 "pikchr.c" | |
| 2400 | - yymsp[-2].minor.yy227 = yylhsminor.yy227; | |
| 2403 | +#line 553 "pikchr.y" | |
| 2404 | +{ yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } | |
| 2405 | +#line 2430 "pikchr.c" | |
| 2406 | + yymsp[-2].minor.yy235 = yylhsminor.yy235; | |
| 2401 | 2407 | break; |
| 2402 | 2408 | case 3: /* statement ::= */ |
| 2403 | -#line 555 "pikchr.y" | |
| 2404 | -{ yymsp[1].minor.yy36 = 0; } | |
| 2405 | -#line 2430 "pikchr.c" | |
| 2409 | +#line 556 "pikchr.y" | |
| 2410 | +{ yymsp[1].minor.yy162 = 0; } | |
| 2411 | +#line 2436 "pikchr.c" | |
| 2406 | 2412 | break; |
| 2407 | 2413 | case 4: /* statement ::= direction */ |
| 2408 | -#line 556 "pikchr.y" | |
| 2409 | -{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy36=0; } | |
| 2410 | -#line 2435 "pikchr.c" | |
| 2411 | - yymsp[0].minor.yy36 = yylhsminor.yy36; | |
| 2414 | +#line 557 "pikchr.y" | |
| 2415 | +{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } | |
| 2416 | +#line 2441 "pikchr.c" | |
| 2417 | + yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2412 | 2418 | break; |
| 2413 | 2419 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2414 | -#line 557 "pikchr.y" | |
| 2415 | -{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy153,&yymsp[-1].minor.yy0); yylhsminor.yy36=0;} | |
| 2416 | -#line 2441 "pikchr.c" | |
| 2417 | - yymsp[-2].minor.yy36 = yylhsminor.yy36; | |
| 2420 | +#line 558 "pikchr.y" | |
| 2421 | +{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} | |
| 2422 | +#line 2447 "pikchr.c" | |
| 2423 | + yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2418 | 2424 | break; |
| 2419 | 2425 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2420 | -#line 559 "pikchr.y" | |
| 2421 | -{ yylhsminor.yy36 = yymsp[0].minor.yy36; pik_elem_setname(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0); } | |
| 2422 | -#line 2447 "pikchr.c" | |
| 2423 | - yymsp[-2].minor.yy36 = yylhsminor.yy36; | |
| 2426 | +#line 560 "pikchr.y" | |
| 2427 | +{ yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } | |
| 2428 | +#line 2453 "pikchr.c" | |
| 2429 | + yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2424 | 2430 | break; |
| 2425 | 2431 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2426 | -#line 561 "pikchr.y" | |
| 2427 | -{ yylhsminor.yy36 = pik_elem_new(p,0,0,0); | |
| 2428 | - if(yylhsminor.yy36){ yylhsminor.yy36->ptAt = yymsp[0].minor.yy79; pik_elem_setname(p,yylhsminor.yy36,&yymsp[-2].minor.yy0); }} | |
| 2429 | -#line 2454 "pikchr.c" | |
| 2430 | - yymsp[-2].minor.yy36 = yylhsminor.yy36; | |
| 2432 | +#line 562 "pikchr.y" | |
| 2433 | +{ yylhsminor.yy162 = pik_elem_new(p,0,0,0); | |
| 2434 | + if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} | |
| 2435 | +#line 2460 "pikchr.c" | |
| 2436 | + yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2431 | 2437 | break; |
| 2432 | 2438 | case 8: /* statement ::= unnamed_statement */ |
| 2433 | -#line 563 "pikchr.y" | |
| 2434 | -{yylhsminor.yy36 = yymsp[0].minor.yy36;} | |
| 2435 | -#line 2460 "pikchr.c" | |
| 2436 | - yymsp[0].minor.yy36 = yylhsminor.yy36; | |
| 2439 | +#line 564 "pikchr.y" | |
| 2440 | +{yylhsminor.yy162 = yymsp[0].minor.yy162;} | |
| 2441 | +#line 2466 "pikchr.c" | |
| 2442 | + yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2437 | 2443 | break; |
| 2438 | 2444 | case 9: /* statement ::= print prlist */ |
| 2439 | -#line 564 "pikchr.y" | |
| 2440 | -{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy36=0;} | |
| 2441 | -#line 2466 "pikchr.c" | |
| 2445 | +#line 565 "pikchr.y" | |
| 2446 | +{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;} | |
| 2447 | +#line 2472 "pikchr.c" | |
| 2442 | 2448 | break; |
| 2443 | 2449 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2444 | -#line 569 "pikchr.y" | |
| 2445 | -{yymsp[-5].minor.yy36=pik_assert(p,yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy153);} | |
| 2446 | -#line 2471 "pikchr.c" | |
| 2450 | +#line 570 "pikchr.y" | |
| 2451 | +{yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} | |
| 2452 | +#line 2477 "pikchr.c" | |
| 2447 | 2453 | break; |
| 2448 | 2454 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2449 | -#line 571 "pikchr.y" | |
| 2450 | -{yymsp[-5].minor.yy36=pik_position_assert(p,&yymsp[-3].minor.yy79,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy79);} | |
| 2451 | -#line 2476 "pikchr.c" | |
| 2455 | +#line 572 "pikchr.y" | |
| 2456 | +{yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} | |
| 2457 | +#line 2482 "pikchr.c" | |
| 2452 | 2458 | break; |
| 2453 | 2459 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2454 | -#line 572 "pikchr.y" | |
| 2455 | -{yymsp[-2].minor.yy36=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} | |
| 2456 | -#line 2481 "pikchr.c" | |
| 2460 | +#line 573 "pikchr.y" | |
| 2461 | +{yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} | |
| 2462 | +#line 2487 "pikchr.c" | |
| 2457 | 2463 | break; |
| 2458 | 2464 | case 13: /* rvalue ::= PLACENAME */ |
| 2459 | -#line 583 "pikchr.y" | |
| 2460 | -{yylhsminor.yy153 = pik_lookup_color(p,&yymsp[0].minor.yy0);} | |
| 2461 | -#line 2486 "pikchr.c" | |
| 2462 | - yymsp[0].minor.yy153 = yylhsminor.yy153; | |
| 2465 | +#line 584 "pikchr.y" | |
| 2466 | +{yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} | |
| 2467 | +#line 2492 "pikchr.c" | |
| 2468 | + yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 2463 | 2469 | break; |
| 2464 | 2470 | case 14: /* pritem ::= FILL */ |
| 2465 | 2471 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2466 | 2472 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2467 | -#line 588 "pikchr.y" | |
| 2473 | +#line 589 "pikchr.y" | |
| 2468 | 2474 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2469 | -#line 2494 "pikchr.c" | |
| 2475 | +#line 2500 "pikchr.c" | |
| 2470 | 2476 | break; |
| 2471 | 2477 | case 17: /* pritem ::= rvalue */ |
| 2472 | -#line 591 "pikchr.y" | |
| 2473 | -{pik_append_num(p,"",yymsp[0].minor.yy153);} | |
| 2474 | -#line 2499 "pikchr.c" | |
| 2478 | +#line 592 "pikchr.y" | |
| 2479 | +{pik_append_num(p,"",yymsp[0].minor.yy21);} | |
| 2480 | +#line 2505 "pikchr.c" | |
| 2475 | 2481 | break; |
| 2476 | 2482 | case 18: /* pritem ::= STRING */ |
| 2477 | -#line 592 "pikchr.y" | |
| 2483 | +#line 593 "pikchr.y" | |
| 2478 | 2484 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2479 | -#line 2504 "pikchr.c" | |
| 2485 | +#line 2510 "pikchr.c" | |
| 2480 | 2486 | break; |
| 2481 | 2487 | case 19: /* prsep ::= COMMA */ |
| 2482 | -#line 593 "pikchr.y" | |
| 2488 | +#line 594 "pikchr.y" | |
| 2483 | 2489 | {pik_append(p, " ", 1);} |
| 2484 | -#line 2509 "pikchr.c" | |
| 2490 | +#line 2515 "pikchr.c" | |
| 2485 | 2491 | break; |
| 2486 | 2492 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2487 | -#line 596 "pikchr.y" | |
| 2488 | -{yylhsminor.yy36 = yymsp[-1].minor.yy36; pik_after_adding_attributes(p,yylhsminor.yy36);} | |
| 2489 | -#line 2514 "pikchr.c" | |
| 2490 | - yymsp[-1].minor.yy36 = yylhsminor.yy36; | |
| 2493 | +#line 597 "pikchr.y" | |
| 2494 | +{yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} | |
| 2495 | +#line 2520 "pikchr.c" | |
| 2496 | + yymsp[-1].minor.yy162 = yylhsminor.yy162; | |
| 2491 | 2497 | break; |
| 2492 | 2498 | case 21: /* basetype ::= CLASSNAME */ |
| 2493 | -#line 598 "pikchr.y" | |
| 2494 | -{yylhsminor.yy36 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } | |
| 2495 | -#line 2520 "pikchr.c" | |
| 2496 | - yymsp[0].minor.yy36 = yylhsminor.yy36; | |
| 2499 | +#line 599 "pikchr.y" | |
| 2500 | +{yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } | |
| 2501 | +#line 2526 "pikchr.c" | |
| 2502 | + yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2497 | 2503 | break; |
| 2498 | 2504 | case 22: /* basetype ::= STRING textposition */ |
| 2499 | -#line 600 "pikchr.y" | |
| 2500 | -{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy164; yylhsminor.yy36 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } | |
| 2501 | -#line 2526 "pikchr.c" | |
| 2502 | - yymsp[-1].minor.yy36 = yylhsminor.yy36; | |
| 2505 | +#line 601 "pikchr.y" | |
| 2506 | +{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } | |
| 2507 | +#line 2532 "pikchr.c" | |
| 2508 | + yymsp[-1].minor.yy162 = yylhsminor.yy162; | |
| 2503 | 2509 | break; |
| 2504 | 2510 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2505 | -#line 602 "pikchr.y" | |
| 2506 | -{ p->list = yymsp[-2].minor.yy227; yymsp[-3].minor.yy36 = pik_elem_new(p,0,0,yymsp[-1].minor.yy227); if(yymsp[-3].minor.yy36) yymsp[-3].minor.yy36->errTok = yymsp[0].minor.yy0; } | |
| 2507 | -#line 2532 "pikchr.c" | |
| 2511 | +#line 603 "pikchr.y" | |
| 2512 | +{ p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } | |
| 2513 | +#line 2538 "pikchr.c" | |
| 2508 | 2514 | break; |
| 2509 | 2515 | case 24: /* savelist ::= */ |
| 2510 | -#line 607 "pikchr.y" | |
| 2511 | -{yymsp[1].minor.yy227 = p->list; p->list = 0;} | |
| 2512 | -#line 2537 "pikchr.c" | |
| 2516 | +#line 608 "pikchr.y" | |
| 2517 | +{yymsp[1].minor.yy235 = p->list; p->list = 0;} | |
| 2518 | +#line 2543 "pikchr.c" | |
| 2513 | 2519 | break; |
| 2514 | 2520 | case 25: /* relexpr ::= expr */ |
| 2515 | -#line 614 "pikchr.y" | |
| 2516 | -{yylhsminor.yy10.rAbs = yymsp[0].minor.yy153; yylhsminor.yy10.rRel = 0;} | |
| 2517 | -#line 2542 "pikchr.c" | |
| 2518 | - yymsp[0].minor.yy10 = yylhsminor.yy10; | |
| 2521 | +#line 615 "pikchr.y" | |
| 2522 | +{yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} | |
| 2523 | +#line 2548 "pikchr.c" | |
| 2524 | + yymsp[0].minor.yy72 = yylhsminor.yy72; | |
| 2519 | 2525 | break; |
| 2520 | 2526 | case 26: /* relexpr ::= expr PERCENT */ |
| 2521 | -#line 615 "pikchr.y" | |
| 2522 | -{yylhsminor.yy10.rAbs = 0; yylhsminor.yy10.rRel = yymsp[-1].minor.yy153/100;} | |
| 2523 | -#line 2548 "pikchr.c" | |
| 2524 | - yymsp[-1].minor.yy10 = yylhsminor.yy10; | |
| 2527 | +#line 616 "pikchr.y" | |
| 2528 | +{yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} | |
| 2529 | +#line 2554 "pikchr.c" | |
| 2530 | + yymsp[-1].minor.yy72 = yylhsminor.yy72; | |
| 2525 | 2531 | break; |
| 2526 | 2532 | case 27: /* optrelexpr ::= */ |
| 2527 | -#line 617 "pikchr.y" | |
| 2528 | -{yymsp[1].minor.yy10.rAbs = 0; yymsp[1].minor.yy10.rRel = 1.0;} | |
| 2529 | -#line 2554 "pikchr.c" | |
| 2533 | +#line 618 "pikchr.y" | |
| 2534 | +{yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} | |
| 2535 | +#line 2560 "pikchr.c" | |
| 2530 | 2536 | break; |
| 2531 | 2537 | case 28: /* attribute_list ::= relexpr alist */ |
| 2532 | -#line 619 "pikchr.y" | |
| 2533 | -{pik_add_direction(p,0,&yymsp[-1].minor.yy10);} | |
| 2534 | -#line 2559 "pikchr.c" | |
| 2538 | +#line 620 "pikchr.y" | |
| 2539 | +{pik_add_direction(p,0,&yymsp[-1].minor.yy72);} | |
| 2540 | +#line 2565 "pikchr.c" | |
| 2535 | 2541 | break; |
| 2536 | 2542 | case 29: /* attribute ::= numproperty relexpr */ |
| 2537 | -#line 623 "pikchr.y" | |
| 2538 | -{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10); } | |
| 2539 | -#line 2564 "pikchr.c" | |
| 2543 | +#line 624 "pikchr.y" | |
| 2544 | +{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } | |
| 2545 | +#line 2570 "pikchr.c" | |
| 2540 | 2546 | break; |
| 2541 | 2547 | case 30: /* attribute ::= dashproperty expr */ |
| 2542 | -#line 624 "pikchr.y" | |
| 2543 | -{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy153); } | |
| 2544 | -#line 2569 "pikchr.c" | |
| 2548 | +#line 625 "pikchr.y" | |
| 2549 | +{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } | |
| 2550 | +#line 2575 "pikchr.c" | |
| 2545 | 2551 | break; |
| 2546 | 2552 | case 31: /* attribute ::= dashproperty */ |
| 2547 | -#line 625 "pikchr.y" | |
| 2553 | +#line 626 "pikchr.y" | |
| 2548 | 2554 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2549 | -#line 2574 "pikchr.c" | |
| 2555 | +#line 2580 "pikchr.c" | |
| 2550 | 2556 | break; |
| 2551 | 2557 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2552 | -#line 626 "pikchr.y" | |
| 2553 | -{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153); } | |
| 2554 | -#line 2579 "pikchr.c" | |
| 2558 | +#line 627 "pikchr.y" | |
| 2559 | +{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } | |
| 2560 | +#line 2585 "pikchr.c" | |
| 2555 | 2561 | break; |
| 2556 | 2562 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2557 | -#line 627 "pikchr.y" | |
| 2558 | -{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10);} | |
| 2559 | -#line 2584 "pikchr.c" | |
| 2563 | +#line 628 "pikchr.y" | |
| 2564 | +{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} | |
| 2565 | +#line 2590 "pikchr.c" | |
| 2560 | 2566 | break; |
| 2561 | 2567 | case 34: /* attribute ::= go direction even position */ |
| 2562 | -#line 628 "pikchr.y" | |
| 2563 | -{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79);} | |
| 2564 | -#line 2589 "pikchr.c" | |
| 2568 | +#line 629 "pikchr.y" | |
| 2569 | +{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} | |
| 2570 | +#line 2595 "pikchr.c" | |
| 2565 | 2571 | break; |
| 2566 | 2572 | case 35: /* attribute ::= CLOSE */ |
| 2567 | -#line 629 "pikchr.y" | |
| 2573 | +#line 630 "pikchr.y" | |
| 2568 | 2574 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2569 | -#line 2594 "pikchr.c" | |
| 2575 | +#line 2600 "pikchr.c" | |
| 2570 | 2576 | break; |
| 2571 | 2577 | case 36: /* attribute ::= CHOP */ |
| 2572 | -#line 630 "pikchr.y" | |
| 2578 | +#line 631 "pikchr.y" | |
| 2573 | 2579 | { p->cur->bChop = 1; } |
| 2574 | -#line 2599 "pikchr.c" | |
| 2580 | +#line 2605 "pikchr.c" | |
| 2575 | 2581 | break; |
| 2576 | 2582 | case 37: /* attribute ::= FROM position */ |
| 2577 | -#line 631 "pikchr.y" | |
| 2578 | -{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); } | |
| 2579 | -#line 2604 "pikchr.c" | |
| 2583 | +#line 632 "pikchr.y" | |
| 2584 | +{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } | |
| 2585 | +#line 2610 "pikchr.c" | |
| 2580 | 2586 | break; |
| 2581 | 2587 | case 38: /* attribute ::= TO position */ |
| 2582 | -#line 632 "pikchr.y" | |
| 2583 | -{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); } | |
| 2584 | -#line 2609 "pikchr.c" | |
| 2588 | +#line 633 "pikchr.y" | |
| 2589 | +{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } | |
| 2590 | +#line 2615 "pikchr.c" | |
| 2585 | 2591 | break; |
| 2586 | 2592 | case 39: /* attribute ::= THEN */ |
| 2587 | -#line 633 "pikchr.y" | |
| 2593 | +#line 634 "pikchr.y" | |
| 2588 | 2594 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2589 | -#line 2614 "pikchr.c" | |
| 2595 | +#line 2620 "pikchr.c" | |
| 2590 | 2596 | break; |
| 2591 | 2597 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2592 | 2598 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2593 | -#line 635 "pikchr.y" | |
| 2594 | -{pik_move_hdg(p,&yymsp[-2].minor.yy10,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153,0,&yymsp[-3].minor.yy0);} | |
| 2595 | -#line 2620 "pikchr.c" | |
| 2599 | +#line 636 "pikchr.y" | |
| 2600 | +{pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} | |
| 2601 | +#line 2626 "pikchr.c" | |
| 2596 | 2602 | break; |
| 2597 | 2603 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2598 | 2604 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2599 | -#line 636 "pikchr.y" | |
| 2600 | -{pik_move_hdg(p,&yymsp[-1].minor.yy10,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} | |
| 2601 | -#line 2626 "pikchr.c" | |
| 2605 | +#line 637 "pikchr.y" | |
| 2606 | +{pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} | |
| 2607 | +#line 2632 "pikchr.c" | |
| 2602 | 2608 | break; |
| 2603 | 2609 | case 44: /* attribute ::= AT position */ |
| 2604 | -#line 641 "pikchr.y" | |
| 2605 | -{ pik_set_at(p,0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); } | |
| 2606 | -#line 2631 "pikchr.c" | |
| 2610 | +#line 642 "pikchr.y" | |
| 2611 | +{ pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } | |
| 2612 | +#line 2637 "pikchr.c" | |
| 2607 | 2613 | break; |
| 2608 | 2614 | case 45: /* attribute ::= SAME */ |
| 2609 | -#line 643 "pikchr.y" | |
| 2615 | +#line 644 "pikchr.y" | |
| 2610 | 2616 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2611 | -#line 2636 "pikchr.c" | |
| 2617 | +#line 2642 "pikchr.c" | |
| 2612 | 2618 | break; |
| 2613 | 2619 | case 46: /* attribute ::= SAME AS object */ |
| 2614 | -#line 644 "pikchr.y" | |
| 2615 | -{pik_same(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} | |
| 2616 | -#line 2641 "pikchr.c" | |
| 2620 | +#line 645 "pikchr.y" | |
| 2621 | +{pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2622 | +#line 2647 "pikchr.c" | |
| 2617 | 2623 | break; |
| 2618 | 2624 | case 47: /* attribute ::= STRING textposition */ |
| 2619 | -#line 645 "pikchr.y" | |
| 2620 | -{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy164);} | |
| 2621 | -#line 2646 "pikchr.c" | |
| 2625 | +#line 646 "pikchr.y" | |
| 2626 | +{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} | |
| 2627 | +#line 2652 "pikchr.c" | |
| 2622 | 2628 | break; |
| 2623 | 2629 | case 48: /* attribute ::= FIT */ |
| 2624 | -#line 646 "pikchr.y" | |
| 2630 | +#line 647 "pikchr.y" | |
| 2625 | 2631 | {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } |
| 2626 | -#line 2651 "pikchr.c" | |
| 2632 | +#line 2657 "pikchr.c" | |
| 2627 | 2633 | break; |
| 2628 | 2634 | case 49: /* attribute ::= BEHIND object */ |
| 2629 | -#line 647 "pikchr.y" | |
| 2630 | -{pik_behind(p,yymsp[0].minor.yy36);} | |
| 2631 | -#line 2656 "pikchr.c" | |
| 2635 | +#line 648 "pikchr.y" | |
| 2636 | +{pik_behind(p,yymsp[0].minor.yy162);} | |
| 2637 | +#line 2662 "pikchr.c" | |
| 2632 | 2638 | break; |
| 2633 | 2639 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2634 | 2640 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2635 | -#line 655 "pikchr.y" | |
| 2636 | -{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); } | |
| 2637 | -#line 2662 "pikchr.c" | |
| 2641 | +#line 656 "pikchr.y" | |
| 2642 | +{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } | |
| 2643 | +#line 2668 "pikchr.c" | |
| 2638 | 2644 | break; |
| 2639 | 2645 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2640 | -#line 659 "pikchr.y" | |
| 2646 | +#line 660 "pikchr.y" | |
| 2641 | 2647 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2642 | -#line 2667 "pikchr.c" | |
| 2648 | +#line 2673 "pikchr.c" | |
| 2643 | 2649 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2644 | 2650 | break; |
| 2645 | 2651 | case 53: /* boolproperty ::= CW */ |
| 2646 | -#line 670 "pikchr.y" | |
| 2652 | +#line 671 "pikchr.y" | |
| 2647 | 2653 | {p->cur->cw = 1;} |
| 2648 | -#line 2673 "pikchr.c" | |
| 2654 | +#line 2679 "pikchr.c" | |
| 2649 | 2655 | break; |
| 2650 | 2656 | case 54: /* boolproperty ::= CCW */ |
| 2651 | -#line 671 "pikchr.y" | |
| 2657 | +#line 672 "pikchr.y" | |
| 2652 | 2658 | {p->cur->cw = 0;} |
| 2653 | -#line 2678 "pikchr.c" | |
| 2659 | +#line 2684 "pikchr.c" | |
| 2654 | 2660 | break; |
| 2655 | 2661 | case 55: /* boolproperty ::= LARROW */ |
| 2656 | -#line 672 "pikchr.y" | |
| 2662 | +#line 673 "pikchr.y" | |
| 2657 | 2663 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2658 | -#line 2683 "pikchr.c" | |
| 2664 | +#line 2689 "pikchr.c" | |
| 2659 | 2665 | break; |
| 2660 | 2666 | case 56: /* boolproperty ::= RARROW */ |
| 2661 | -#line 673 "pikchr.y" | |
| 2667 | +#line 674 "pikchr.y" | |
| 2662 | 2668 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2663 | -#line 2688 "pikchr.c" | |
| 2669 | +#line 2694 "pikchr.c" | |
| 2664 | 2670 | break; |
| 2665 | 2671 | case 57: /* boolproperty ::= LRARROW */ |
| 2666 | -#line 674 "pikchr.y" | |
| 2672 | +#line 675 "pikchr.y" | |
| 2667 | 2673 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2668 | -#line 2693 "pikchr.c" | |
| 2674 | +#line 2699 "pikchr.c" | |
| 2669 | 2675 | break; |
| 2670 | 2676 | case 58: /* boolproperty ::= INVIS */ |
| 2671 | -#line 675 "pikchr.y" | |
| 2677 | +#line 676 "pikchr.y" | |
| 2672 | 2678 | {p->cur->sw = 0.0;} |
| 2673 | -#line 2698 "pikchr.c" | |
| 2679 | +#line 2704 "pikchr.c" | |
| 2674 | 2680 | break; |
| 2675 | 2681 | case 59: /* boolproperty ::= THICK */ |
| 2676 | -#line 676 "pikchr.y" | |
| 2682 | +#line 677 "pikchr.y" | |
| 2677 | 2683 | {p->cur->sw *= 1.5;} |
| 2678 | -#line 2703 "pikchr.c" | |
| 2684 | +#line 2709 "pikchr.c" | |
| 2679 | 2685 | break; |
| 2680 | 2686 | case 60: /* boolproperty ::= THIN */ |
| 2681 | -#line 677 "pikchr.y" | |
| 2687 | +#line 678 "pikchr.y" | |
| 2682 | 2688 | {p->cur->sw *= 0.67;} |
| 2683 | -#line 2708 "pikchr.c" | |
| 2689 | +#line 2714 "pikchr.c" | |
| 2684 | 2690 | break; |
| 2685 | 2691 | case 61: /* boolproperty ::= SOLID */ |
| 2686 | -#line 678 "pikchr.y" | |
| 2692 | +#line 679 "pikchr.y" | |
| 2687 | 2693 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2688 | 2694 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2689 | -#line 2714 "pikchr.c" | |
| 2695 | +#line 2720 "pikchr.c" | |
| 2690 | 2696 | break; |
| 2691 | 2697 | case 62: /* textposition ::= */ |
| 2692 | -#line 681 "pikchr.y" | |
| 2693 | -{yymsp[1].minor.yy164 = 0;} | |
| 2694 | -#line 2719 "pikchr.c" | |
| 2698 | +#line 682 "pikchr.y" | |
| 2699 | +{yymsp[1].minor.yy188 = 0;} | |
| 2700 | +#line 2725 "pikchr.c" | |
| 2695 | 2701 | break; |
| 2696 | - case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ | |
| 2697 | -#line 684 "pikchr.y" | |
| 2698 | -{yylhsminor.yy164 = (short int)pik_text_position(yymsp[-1].minor.yy164,&yymsp[0].minor.yy0);} | |
| 2699 | -#line 2724 "pikchr.c" | |
| 2700 | - yymsp[-1].minor.yy164 = yylhsminor.yy164; | |
| 2702 | + case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ | |
| 2703 | +#line 685 "pikchr.y" | |
| 2704 | +{yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} | |
| 2705 | +#line 2730 "pikchr.c" | |
| 2706 | + yymsp[-1].minor.yy188 = yylhsminor.yy188; | |
| 2701 | 2707 | break; |
| 2702 | 2708 | case 64: /* position ::= expr COMMA expr */ |
| 2703 | -#line 687 "pikchr.y" | |
| 2704 | -{yylhsminor.yy79.x=yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[0].minor.yy153;} | |
| 2705 | -#line 2730 "pikchr.c" | |
| 2706 | - yymsp[-2].minor.yy79 = yylhsminor.yy79; | |
| 2709 | +#line 688 "pikchr.y" | |
| 2710 | +{yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} | |
| 2711 | +#line 2736 "pikchr.c" | |
| 2712 | + yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2707 | 2713 | break; |
| 2708 | 2714 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2709 | -#line 689 "pikchr.y" | |
| 2710 | -{yylhsminor.yy79.x=yymsp[-4].minor.yy79.x+yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y+yymsp[0].minor.yy153;} | |
| 2711 | -#line 2736 "pikchr.c" | |
| 2712 | - yymsp[-4].minor.yy79 = yylhsminor.yy79; | |
| 2715 | +#line 690 "pikchr.y" | |
| 2716 | +{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} | |
| 2717 | +#line 2742 "pikchr.c" | |
| 2718 | + yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2713 | 2719 | break; |
| 2714 | 2720 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2715 | -#line 690 "pikchr.y" | |
| 2716 | -{yylhsminor.yy79.x=yymsp[-4].minor.yy79.x-yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y-yymsp[0].minor.yy153;} | |
| 2717 | -#line 2742 "pikchr.c" | |
| 2718 | - yymsp[-4].minor.yy79 = yylhsminor.yy79; | |
| 2721 | +#line 691 "pikchr.y" | |
| 2722 | +{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} | |
| 2723 | +#line 2748 "pikchr.c" | |
| 2724 | + yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2719 | 2725 | break; |
| 2720 | 2726 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2721 | -#line 692 "pikchr.y" | |
| 2722 | -{yylhsminor.yy79.x=yymsp[-6].minor.yy79.x+yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y+yymsp[-1].minor.yy153;} | |
| 2723 | -#line 2748 "pikchr.c" | |
| 2724 | - yymsp[-6].minor.yy79 = yylhsminor.yy79; | |
| 2727 | +#line 693 "pikchr.y" | |
| 2728 | +{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} | |
| 2729 | +#line 2754 "pikchr.c" | |
| 2730 | + yymsp[-6].minor.yy63 = yylhsminor.yy63; | |
| 2725 | 2731 | break; |
| 2726 | 2732 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2727 | -#line 694 "pikchr.y" | |
| 2728 | -{yylhsminor.yy79.x=yymsp[-6].minor.yy79.x-yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y-yymsp[-1].minor.yy153;} | |
| 2729 | -#line 2754 "pikchr.c" | |
| 2730 | - yymsp[-6].minor.yy79 = yylhsminor.yy79; | |
| 2733 | +#line 695 "pikchr.y" | |
| 2734 | +{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} | |
| 2735 | +#line 2760 "pikchr.c" | |
| 2736 | + yymsp[-6].minor.yy63 = yylhsminor.yy63; | |
| 2731 | 2737 | break; |
| 2732 | 2738 | case 69: /* position ::= LP position COMMA position RP */ |
| 2733 | -#line 695 "pikchr.y" | |
| 2734 | -{yymsp[-4].minor.yy79.x=yymsp[-3].minor.yy79.x; yymsp[-4].minor.yy79.y=yymsp[-1].minor.yy79.y;} | |
| 2735 | -#line 2760 "pikchr.c" | |
| 2739 | +#line 696 "pikchr.y" | |
| 2740 | +{yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} | |
| 2741 | +#line 2766 "pikchr.c" | |
| 2736 | 2742 | break; |
| 2737 | 2743 | case 70: /* position ::= LP position RP */ |
| 2738 | -#line 696 "pikchr.y" | |
| 2739 | -{yymsp[-2].minor.yy79=yymsp[-1].minor.yy79;} | |
| 2740 | -#line 2765 "pikchr.c" | |
| 2744 | +#line 697 "pikchr.y" | |
| 2745 | +{yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} | |
| 2746 | +#line 2771 "pikchr.c" | |
| 2741 | 2747 | break; |
| 2742 | 2748 | case 71: /* position ::= expr between position AND position */ |
| 2743 | -#line 698 "pikchr.y" | |
| 2744 | -{yylhsminor.yy79 = pik_position_between(yymsp[-4].minor.yy153,yymsp[-2].minor.yy79,yymsp[0].minor.yy79);} | |
| 2745 | -#line 2770 "pikchr.c" | |
| 2746 | - yymsp[-4].minor.yy79 = yylhsminor.yy79; | |
| 2749 | +#line 699 "pikchr.y" | |
| 2750 | +{yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} | |
| 2751 | +#line 2776 "pikchr.c" | |
| 2752 | + yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2747 | 2753 | break; |
| 2748 | 2754 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2749 | -#line 700 "pikchr.y" | |
| 2750 | -{yylhsminor.yy79 = pik_position_between(yymsp[-5].minor.yy153,yymsp[-3].minor.yy79,yymsp[-1].minor.yy79);} | |
| 2751 | -#line 2776 "pikchr.c" | |
| 2752 | - yymsp[-5].minor.yy79 = yylhsminor.yy79; | |
| 2755 | +#line 701 "pikchr.y" | |
| 2756 | +{yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} | |
| 2757 | +#line 2782 "pikchr.c" | |
| 2758 | + yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2753 | 2759 | break; |
| 2754 | 2760 | case 73: /* position ::= expr ABOVE position */ |
| 2755 | -#line 701 "pikchr.y" | |
| 2756 | -{yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y += yymsp[-2].minor.yy153;} | |
| 2757 | -#line 2782 "pikchr.c" | |
| 2758 | - yymsp[-2].minor.yy79 = yylhsminor.yy79; | |
| 2761 | +#line 702 "pikchr.y" | |
| 2762 | +{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} | |
| 2763 | +#line 2788 "pikchr.c" | |
| 2764 | + yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2759 | 2765 | break; |
| 2760 | 2766 | case 74: /* position ::= expr BELOW position */ |
| 2761 | -#line 702 "pikchr.y" | |
| 2762 | -{yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y -= yymsp[-2].minor.yy153;} | |
| 2763 | -#line 2788 "pikchr.c" | |
| 2764 | - yymsp[-2].minor.yy79 = yylhsminor.yy79; | |
| 2767 | +#line 703 "pikchr.y" | |
| 2768 | +{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} | |
| 2769 | +#line 2794 "pikchr.c" | |
| 2770 | + yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2765 | 2771 | break; |
| 2766 | 2772 | case 75: /* position ::= expr LEFT OF position */ |
| 2767 | -#line 703 "pikchr.y" | |
| 2768 | -{yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x -= yymsp[-3].minor.yy153;} | |
| 2769 | -#line 2794 "pikchr.c" | |
| 2770 | - yymsp[-3].minor.yy79 = yylhsminor.yy79; | |
| 2773 | +#line 704 "pikchr.y" | |
| 2774 | +{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} | |
| 2775 | +#line 2800 "pikchr.c" | |
| 2776 | + yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2771 | 2777 | break; |
| 2772 | 2778 | case 76: /* position ::= expr RIGHT OF position */ |
| 2773 | -#line 704 "pikchr.y" | |
| 2774 | -{yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x += yymsp[-3].minor.yy153;} | |
| 2775 | -#line 2800 "pikchr.c" | |
| 2776 | - yymsp[-3].minor.yy79 = yylhsminor.yy79; | |
| 2779 | +#line 705 "pikchr.y" | |
| 2780 | +{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} | |
| 2781 | +#line 2806 "pikchr.c" | |
| 2782 | + yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2777 | 2783 | break; |
| 2778 | 2784 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2779 | -#line 706 "pikchr.y" | |
| 2780 | -{yylhsminor.yy79 = pik_position_at_hdg(yymsp[-5].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} | |
| 2781 | -#line 2806 "pikchr.c" | |
| 2782 | - yymsp[-5].minor.yy79 = yylhsminor.yy79; | |
| 2785 | +#line 707 "pikchr.y" | |
| 2786 | +{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2787 | +#line 2812 "pikchr.c" | |
| 2788 | + yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2783 | 2789 | break; |
| 2784 | 2790 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2785 | -#line 708 "pikchr.y" | |
| 2786 | -{yylhsminor.yy79 = pik_position_at_hdg(yymsp[-4].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} | |
| 2787 | -#line 2812 "pikchr.c" | |
| 2788 | - yymsp[-4].minor.yy79 = yylhsminor.yy79; | |
| 2791 | +#line 709 "pikchr.y" | |
| 2792 | +{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2793 | +#line 2818 "pikchr.c" | |
| 2794 | + yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2789 | 2795 | break; |
| 2790 | 2796 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2791 | -#line 710 "pikchr.y" | |
| 2792 | -{yylhsminor.yy79 = pik_position_at_hdg(yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} | |
| 2793 | -#line 2818 "pikchr.c" | |
| 2794 | - yymsp[-3].minor.yy79 = yylhsminor.yy79; | |
| 2797 | +#line 711 "pikchr.y" | |
| 2798 | +{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2799 | +#line 2824 "pikchr.c" | |
| 2800 | + yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2795 | 2801 | break; |
| 2796 | 2802 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2797 | -#line 712 "pikchr.y" | |
| 2798 | -{yylhsminor.yy79 = pik_position_at_angle(yymsp[-5].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);} | |
| 2799 | -#line 2824 "pikchr.c" | |
| 2800 | - yymsp[-5].minor.yy79 = yylhsminor.yy79; | |
| 2803 | +#line 713 "pikchr.y" | |
| 2804 | +{yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} | |
| 2805 | +#line 2830 "pikchr.c" | |
| 2806 | + yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2801 | 2807 | break; |
| 2802 | 2808 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2803 | -#line 714 "pikchr.y" | |
| 2804 | -{yylhsminor.yy79 = pik_position_at_angle(yymsp[-4].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);} | |
| 2805 | -#line 2830 "pikchr.c" | |
| 2806 | - yymsp[-4].minor.yy79 = yylhsminor.yy79; | |
| 2809 | +#line 715 "pikchr.y" | |
| 2810 | +{yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} | |
| 2811 | +#line 2836 "pikchr.c" | |
| 2812 | + yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2807 | 2813 | break; |
| 2808 | 2814 | case 82: /* place ::= edge OF object */ |
| 2809 | -#line 726 "pikchr.y" | |
| 2810 | -{yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} | |
| 2811 | -#line 2836 "pikchr.c" | |
| 2812 | - yymsp[-2].minor.yy79 = yylhsminor.yy79; | |
| 2815 | +#line 727 "pikchr.y" | |
| 2816 | +{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2817 | +#line 2842 "pikchr.c" | |
| 2818 | + yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2813 | 2819 | break; |
| 2814 | 2820 | case 83: /* place2 ::= object */ |
| 2815 | -#line 727 "pikchr.y" | |
| 2816 | -{yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,0);} | |
| 2817 | -#line 2842 "pikchr.c" | |
| 2818 | - yymsp[0].minor.yy79 = yylhsminor.yy79; | |
| 2821 | +#line 728 "pikchr.y" | |
| 2822 | +{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} | |
| 2823 | +#line 2848 "pikchr.c" | |
| 2824 | + yymsp[0].minor.yy63 = yylhsminor.yy63; | |
| 2819 | 2825 | break; |
| 2820 | 2826 | case 84: /* place2 ::= object DOT_E edge */ |
| 2821 | -#line 728 "pikchr.y" | |
| 2822 | -{yylhsminor.yy79 = pik_place_of_elem(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} | |
| 2823 | -#line 2848 "pikchr.c" | |
| 2824 | - yymsp[-2].minor.yy79 = yylhsminor.yy79; | |
| 2827 | +#line 729 "pikchr.y" | |
| 2828 | +{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 2829 | +#line 2854 "pikchr.c" | |
| 2830 | + yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2825 | 2831 | break; |
| 2826 | 2832 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2827 | -#line 729 "pikchr.y" | |
| 2828 | -{yylhsminor.yy79 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy36);} | |
| 2829 | -#line 2854 "pikchr.c" | |
| 2830 | - yymsp[-3].minor.yy79 = yylhsminor.yy79; | |
| 2833 | +#line 730 "pikchr.y" | |
| 2834 | +{yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} | |
| 2835 | +#line 2860 "pikchr.c" | |
| 2836 | + yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2831 | 2837 | break; |
| 2832 | 2838 | case 86: /* object ::= nth */ |
| 2833 | -#line 741 "pikchr.y" | |
| 2834 | -{yylhsminor.yy36 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} | |
| 2835 | -#line 2860 "pikchr.c" | |
| 2836 | - yymsp[0].minor.yy36 = yylhsminor.yy36; | |
| 2839 | +#line 742 "pikchr.y" | |
| 2840 | +{yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} | |
| 2841 | +#line 2866 "pikchr.c" | |
| 2842 | + yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2837 | 2843 | break; |
| 2838 | 2844 | case 87: /* object ::= nth OF|IN object */ |
| 2839 | -#line 742 "pikchr.y" | |
| 2840 | -{yylhsminor.yy36 = pik_find_nth(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} | |
| 2841 | -#line 2866 "pikchr.c" | |
| 2842 | - yymsp[-2].minor.yy36 = yylhsminor.yy36; | |
| 2845 | +#line 743 "pikchr.y" | |
| 2846 | +{yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2847 | +#line 2872 "pikchr.c" | |
| 2848 | + yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2843 | 2849 | break; |
| 2844 | 2850 | case 88: /* objectname ::= THIS */ |
| 2845 | -#line 744 "pikchr.y" | |
| 2846 | -{yymsp[0].minor.yy36 = p->cur;} | |
| 2847 | -#line 2872 "pikchr.c" | |
| 2851 | +#line 745 "pikchr.y" | |
| 2852 | +{yymsp[0].minor.yy162 = p->cur;} | |
| 2853 | +#line 2878 "pikchr.c" | |
| 2848 | 2854 | break; |
| 2849 | 2855 | case 89: /* objectname ::= PLACENAME */ |
| 2850 | -#line 745 "pikchr.y" | |
| 2851 | -{yylhsminor.yy36 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} | |
| 2852 | -#line 2877 "pikchr.c" | |
| 2853 | - yymsp[0].minor.yy36 = yylhsminor.yy36; | |
| 2856 | +#line 746 "pikchr.y" | |
| 2857 | +{yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} | |
| 2858 | +#line 2883 "pikchr.c" | |
| 2859 | + yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2854 | 2860 | break; |
| 2855 | 2861 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2856 | -#line 747 "pikchr.y" | |
| 2857 | -{yylhsminor.yy36 = pik_find_byname(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} | |
| 2858 | -#line 2883 "pikchr.c" | |
| 2859 | - yymsp[-2].minor.yy36 = yylhsminor.yy36; | |
| 2862 | +#line 748 "pikchr.y" | |
| 2863 | +{yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 2864 | +#line 2889 "pikchr.c" | |
| 2865 | + yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2860 | 2866 | break; |
| 2861 | 2867 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2862 | -#line 749 "pikchr.y" | |
| 2868 | +#line 750 "pikchr.y" | |
| 2863 | 2869 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2864 | -#line 2889 "pikchr.c" | |
| 2870 | +#line 2895 "pikchr.c" | |
| 2865 | 2871 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2866 | 2872 | break; |
| 2867 | 2873 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2868 | -#line 750 "pikchr.y" | |
| 2874 | +#line 751 "pikchr.y" | |
| 2869 | 2875 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2870 | -#line 2895 "pikchr.c" | |
| 2876 | +#line 2901 "pikchr.c" | |
| 2871 | 2877 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2872 | 2878 | break; |
| 2873 | 2879 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2874 | -#line 751 "pikchr.y" | |
| 2880 | +#line 752 "pikchr.y" | |
| 2875 | 2881 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2876 | -#line 2901 "pikchr.c" | |
| 2882 | +#line 2907 "pikchr.c" | |
| 2877 | 2883 | break; |
| 2878 | 2884 | case 94: /* nth ::= LAST */ |
| 2879 | -#line 752 "pikchr.y" | |
| 2885 | +#line 753 "pikchr.y" | |
| 2880 | 2886 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2881 | -#line 2906 "pikchr.c" | |
| 2887 | +#line 2912 "pikchr.c" | |
| 2882 | 2888 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2883 | 2889 | break; |
| 2884 | 2890 | case 95: /* nth ::= NTH LB RB */ |
| 2885 | -#line 753 "pikchr.y" | |
| 2891 | +#line 754 "pikchr.y" | |
| 2886 | 2892 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2887 | -#line 2912 "pikchr.c" | |
| 2893 | +#line 2918 "pikchr.c" | |
| 2888 | 2894 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2889 | 2895 | break; |
| 2890 | 2896 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2891 | -#line 754 "pikchr.y" | |
| 2897 | +#line 755 "pikchr.y" | |
| 2892 | 2898 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2893 | -#line 2918 "pikchr.c" | |
| 2899 | +#line 2924 "pikchr.c" | |
| 2894 | 2900 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2895 | 2901 | break; |
| 2896 | 2902 | case 97: /* nth ::= LAST LB RB */ |
| 2897 | -#line 755 "pikchr.y" | |
| 2903 | +#line 756 "pikchr.y" | |
| 2898 | 2904 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2899 | -#line 2924 "pikchr.c" | |
| 2905 | +#line 2930 "pikchr.c" | |
| 2900 | 2906 | break; |
| 2901 | 2907 | case 98: /* expr ::= expr PLUS expr */ |
| 2902 | -#line 757 "pikchr.y" | |
| 2903 | -{yylhsminor.yy153=yymsp[-2].minor.yy153+yymsp[0].minor.yy153;} | |
| 2904 | -#line 2929 "pikchr.c" | |
| 2905 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2908 | +#line 758 "pikchr.y" | |
| 2909 | +{yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} | |
| 2910 | +#line 2935 "pikchr.c" | |
| 2911 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2906 | 2912 | break; |
| 2907 | 2913 | case 99: /* expr ::= expr MINUS expr */ |
| 2908 | -#line 758 "pikchr.y" | |
| 2909 | -{yylhsminor.yy153=yymsp[-2].minor.yy153-yymsp[0].minor.yy153;} | |
| 2910 | -#line 2935 "pikchr.c" | |
| 2911 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2914 | +#line 759 "pikchr.y" | |
| 2915 | +{yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} | |
| 2916 | +#line 2941 "pikchr.c" | |
| 2917 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2912 | 2918 | break; |
| 2913 | 2919 | case 100: /* expr ::= expr STAR expr */ |
| 2914 | -#line 759 "pikchr.y" | |
| 2915 | -{yylhsminor.yy153=yymsp[-2].minor.yy153*yymsp[0].minor.yy153;} | |
| 2916 | -#line 2941 "pikchr.c" | |
| 2917 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2920 | +#line 760 "pikchr.y" | |
| 2921 | +{yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} | |
| 2922 | +#line 2947 "pikchr.c" | |
| 2923 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2918 | 2924 | break; |
| 2919 | 2925 | case 101: /* expr ::= expr SLASH expr */ |
| 2920 | -#line 760 "pikchr.y" | |
| 2926 | +#line 761 "pikchr.y" | |
| 2921 | 2927 | { |
| 2922 | - if( yymsp[0].minor.yy153==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy153 = 0.0; } | |
| 2923 | - else{ yylhsminor.yy153 = yymsp[-2].minor.yy153/yymsp[0].minor.yy153; } | |
| 2928 | + if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } | |
| 2929 | + else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } | |
| 2924 | 2930 | } |
| 2925 | -#line 2950 "pikchr.c" | |
| 2926 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2931 | +#line 2956 "pikchr.c" | |
| 2932 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2927 | 2933 | break; |
| 2928 | 2934 | case 102: /* expr ::= MINUS expr */ |
| 2929 | -#line 764 "pikchr.y" | |
| 2930 | -{yymsp[-1].minor.yy153=-yymsp[0].minor.yy153;} | |
| 2931 | -#line 2956 "pikchr.c" | |
| 2935 | +#line 765 "pikchr.y" | |
| 2936 | +{yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} | |
| 2937 | +#line 2962 "pikchr.c" | |
| 2932 | 2938 | break; |
| 2933 | 2939 | case 103: /* expr ::= PLUS expr */ |
| 2934 | -#line 765 "pikchr.y" | |
| 2935 | -{yymsp[-1].minor.yy153=yymsp[0].minor.yy153;} | |
| 2936 | -#line 2961 "pikchr.c" | |
| 2940 | +#line 766 "pikchr.y" | |
| 2941 | +{yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} | |
| 2942 | +#line 2967 "pikchr.c" | |
| 2937 | 2943 | break; |
| 2938 | 2944 | case 104: /* expr ::= LP expr RP */ |
| 2939 | -#line 766 "pikchr.y" | |
| 2940 | -{yymsp[-2].minor.yy153=yymsp[-1].minor.yy153;} | |
| 2941 | -#line 2966 "pikchr.c" | |
| 2945 | +#line 767 "pikchr.y" | |
| 2946 | +{yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} | |
| 2947 | +#line 2972 "pikchr.c" | |
| 2942 | 2948 | break; |
| 2943 | 2949 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2944 | -#line 767 "pikchr.y" | |
| 2945 | -{yymsp[-2].minor.yy153=pik_get_var(p,&yymsp[-1].minor.yy0);} | |
| 2946 | -#line 2971 "pikchr.c" | |
| 2950 | +#line 768 "pikchr.y" | |
| 2951 | +{yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} | |
| 2952 | +#line 2977 "pikchr.c" | |
| 2947 | 2953 | break; |
| 2948 | 2954 | case 106: /* expr ::= NUMBER */ |
| 2949 | -#line 768 "pikchr.y" | |
| 2950 | -{yylhsminor.yy153=pik_atof(&yymsp[0].minor.yy0);} | |
| 2951 | -#line 2976 "pikchr.c" | |
| 2952 | - yymsp[0].minor.yy153 = yylhsminor.yy153; | |
| 2955 | +#line 769 "pikchr.y" | |
| 2956 | +{yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} | |
| 2957 | +#line 2982 "pikchr.c" | |
| 2958 | + yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 2953 | 2959 | break; |
| 2954 | 2960 | case 107: /* expr ::= ID */ |
| 2955 | -#line 769 "pikchr.y" | |
| 2956 | -{yylhsminor.yy153=pik_get_var(p,&yymsp[0].minor.yy0);} | |
| 2957 | -#line 2982 "pikchr.c" | |
| 2958 | - yymsp[0].minor.yy153 = yylhsminor.yy153; | |
| 2961 | +#line 770 "pikchr.y" | |
| 2962 | +{yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} | |
| 2963 | +#line 2988 "pikchr.c" | |
| 2964 | + yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 2959 | 2965 | break; |
| 2960 | 2966 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 2961 | -#line 770 "pikchr.y" | |
| 2962 | -{yylhsminor.yy153 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy153,0.0);} | |
| 2963 | -#line 2988 "pikchr.c" | |
| 2964 | - yymsp[-3].minor.yy153 = yylhsminor.yy153; | |
| 2967 | +#line 771 "pikchr.y" | |
| 2968 | +{yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} | |
| 2969 | +#line 2994 "pikchr.c" | |
| 2970 | + yymsp[-3].minor.yy21 = yylhsminor.yy21; | |
| 2965 | 2971 | break; |
| 2966 | 2972 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2967 | -#line 771 "pikchr.y" | |
| 2968 | -{yylhsminor.yy153 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy153,yymsp[-1].minor.yy153);} | |
| 2969 | -#line 2994 "pikchr.c" | |
| 2970 | - yymsp[-5].minor.yy153 = yylhsminor.yy153; | |
| 2973 | +#line 772 "pikchr.y" | |
| 2974 | +{yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} | |
| 2975 | +#line 3000 "pikchr.c" | |
| 2976 | + yymsp[-5].minor.yy21 = yylhsminor.yy21; | |
| 2971 | 2977 | break; |
| 2972 | 2978 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 2973 | -#line 772 "pikchr.y" | |
| 2974 | -{yymsp[-5].minor.yy153 = pik_dist(&yymsp[-3].minor.yy79,&yymsp[-1].minor.yy79);} | |
| 2975 | -#line 3000 "pikchr.c" | |
| 2979 | +#line 773 "pikchr.y" | |
| 2980 | +{yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} | |
| 2981 | +#line 3006 "pikchr.c" | |
| 2976 | 2982 | break; |
| 2977 | 2983 | case 111: /* expr ::= place2 DOT_XY X */ |
| 2978 | -#line 773 "pikchr.y" | |
| 2979 | -{yylhsminor.yy153 = yymsp[-2].minor.yy79.x;} | |
| 2980 | -#line 3005 "pikchr.c" | |
| 2981 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2984 | +#line 774 "pikchr.y" | |
| 2985 | +{yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} | |
| 2986 | +#line 3011 "pikchr.c" | |
| 2987 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2982 | 2988 | break; |
| 2983 | 2989 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 2984 | -#line 774 "pikchr.y" | |
| 2985 | -{yylhsminor.yy153 = yymsp[-2].minor.yy79.y;} | |
| 2986 | -#line 3011 "pikchr.c" | |
| 2987 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2990 | +#line 775 "pikchr.y" | |
| 2991 | +{yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} | |
| 2992 | +#line 3017 "pikchr.c" | |
| 2993 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2988 | 2994 | break; |
| 2989 | 2995 | case 113: /* expr ::= object DOT_L numproperty */ |
| 2990 | 2996 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 2991 | 2997 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 2992 | -#line 775 "pikchr.y" | |
| 2993 | -{yylhsminor.yy153=pik_property_of(yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} | |
| 2994 | -#line 3019 "pikchr.c" | |
| 2995 | - yymsp[-2].minor.yy153 = yylhsminor.yy153; | |
| 2998 | +#line 776 "pikchr.y" | |
| 2999 | +{yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 3000 | +#line 3025 "pikchr.c" | |
| 3001 | + yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2996 | 3002 | break; |
| 2997 | 3003 | default: |
| 2998 | 3004 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 2999 | 3005 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3000 | 3006 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3092,19 +3098,19 @@ | ||
| 3092 | 3098 | ){ |
| 3093 | 3099 | pik_parserARG_FETCH |
| 3094 | 3100 | pik_parserCTX_FETCH |
| 3095 | 3101 | #define TOKEN yyminor |
| 3096 | 3102 | /************ Begin %syntax_error code ****************************************/ |
| 3097 | -#line 535 "pikchr.y" | |
| 3103 | +#line 536 "pikchr.y" | |
| 3098 | 3104 | |
| 3099 | 3105 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3100 | 3106 | pik_error(p, &TOKEN, "syntax error"); |
| 3101 | 3107 | }else{ |
| 3102 | 3108 | pik_error(p, 0, "syntax error"); |
| 3103 | 3109 | } |
| 3104 | 3110 | UNUSED_PARAMETER(yymajor); |
| 3105 | -#line 3130 "pikchr.c" | |
| 3111 | +#line 3136 "pikchr.c" | |
| 3106 | 3112 | /************ End %syntax_error code ******************************************/ |
| 3107 | 3113 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3108 | 3114 | pik_parserCTX_STORE |
| 3109 | 3115 | } |
| 3110 | 3116 | |
| @@ -3376,11 +3382,11 @@ | ||
| 3376 | 3382 | #else |
| 3377 | 3383 | (void)iToken; |
| 3378 | 3384 | return 0; |
| 3379 | 3385 | #endif |
| 3380 | 3386 | } |
| 3381 | -#line 780 "pikchr.y" | |
| 3387 | +#line 781 "pikchr.y" | |
| 3382 | 3388 | |
| 3383 | 3389 | |
| 3384 | 3390 | |
| 3385 | 3391 | /* Chart of the 148 official CSS color names with their |
| 3386 | 3392 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -4978,14 +4984,16 @@ | ||
| 4978 | 4984 | if( t->eCode & TP_RJUST ) nx += jw; |
| 4979 | 4985 | |
| 4980 | 4986 | if( pBox!=0 ){ |
| 4981 | 4987 | /* If pBox is not NULL, do not draw any <text>. Instead, just expand |
| 4982 | 4988 | ** pBox to include the text */ |
| 4983 | - PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale*0.01; | |
| 4989 | + PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01; | |
| 4984 | 4990 | PNum ch = p->charHeight*0.5*xtraFontScale; |
| 4985 | 4991 | PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ |
| 4986 | - if( t->eCode & TP_BOLD ) cw *= 1.1; | |
| 4992 | + if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){ | |
| 4993 | + cw *= 1.1; | |
| 4994 | + } | |
| 4987 | 4995 | if( t->eCode & TP_RJUST ){ |
| 4988 | 4996 | x0 = nx; |
| 4989 | 4997 | y0 = y-ch; |
| 4990 | 4998 | x1 = nx-cw; |
| 4991 | 4999 | y1 = y+ch; |
| @@ -5037,10 +5045,13 @@ | ||
| 5037 | 5045 | pik_append(p, " font-style=\"italic\"", -1); |
| 5038 | 5046 | } |
| 5039 | 5047 | if( t->eCode & TP_BOLD ){ |
| 5040 | 5048 | pik_append(p, " font-weight=\"bold\"", -1); |
| 5041 | 5049 | } |
| 5050 | + if( t->eCode & TP_MONO ){ | |
| 5051 | + pik_append(p, " font-family=\"monospace\"", -1); | |
| 5052 | + } | |
| 5042 | 5053 | if( pObj->color>=0.0 ){ |
| 5043 | 5054 | pik_append_clr(p, " fill=\"", pObj->color, "\"",0); |
| 5044 | 5055 | } |
| 5045 | 5056 | xtraFontScale *= p->fontScale; |
| 5046 | 5057 | if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ |
| @@ -6044,12 +6055,13 @@ | ||
| 6044 | 6055 | case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; |
| 6045 | 6056 | case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; |
| 6046 | 6057 | case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; |
| 6047 | 6058 | case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; |
| 6048 | 6059 | case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; |
| 6049 | - case T_ITALIC: iRes |= TP_ITALIC; break; | |
| 6050 | - case T_BOLD: iRes |= TP_BOLD; break; | |
| 6060 | + case T_ITALIC: iRes |= TP_ITALIC; break; | |
| 6061 | + case T_BOLD: iRes |= TP_BOLD; break; | |
| 6062 | + case T_MONO: iRes |= TP_MONO; break; | |
| 6051 | 6063 | case T_ALIGNED: iRes |= TP_ALIGN; break; |
| 6052 | 6064 | case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; |
| 6053 | 6065 | else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; |
| 6054 | 6066 | case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; |
| 6055 | 6067 | else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; |
| @@ -6180,15 +6192,17 @@ | ||
| 6180 | 6192 | ** |
| 6181 | 6193 | ** Omit "\" used to escape characters. And count entities like |
| 6182 | 6194 | ** "<" as a single character. Multi-byte UTF8 characters count |
| 6183 | 6195 | ** as a single character. |
| 6184 | 6196 | ** |
| 6185 | -** Attempt to scale the answer by the actual characters seen. Wide | |
| 6186 | -** characters count more than narrow characters. But the widths are | |
| 6187 | -** only guesses. | |
| 6197 | +** Unless using a monospaced font, attempt to scale the answer by | |
| 6198 | +** the actual characters seen. Wide characters count more than | |
| 6199 | +** narrow characters. But the widths are only guesses. | |
| 6200 | +** | |
| 6188 | 6201 | */ |
| 6189 | -static int pik_text_length(const PToken *pToken){ | |
| 6202 | +static int pik_text_length(const PToken *pToken, const int isMonospace){ | |
| 6203 | + const int stdAvg=100, monoAvg=82; | |
| 6190 | 6204 | int n = pToken->n; |
| 6191 | 6205 | const char *z = pToken->z; |
| 6192 | 6206 | int cnt, j; |
| 6193 | 6207 | for(j=1, cnt=0; j<n-1; j++){ |
| 6194 | 6208 | char c = z[j]; |
| @@ -6196,22 +6210,24 @@ | ||
| 6196 | 6210 | c = z[++j]; |
| 6197 | 6211 | }else if( c=='&' ){ |
| 6198 | 6212 | int k; |
| 6199 | 6213 | for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} |
| 6200 | 6214 | if( z[k]==';' ) j = k; |
| 6201 | - cnt += 150; | |
| 6215 | + cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2; | |
| 6202 | 6216 | continue; |
| 6203 | 6217 | } |
| 6204 | 6218 | if( (c & 0xc0)==0xc0 ){ |
| 6205 | 6219 | while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; } |
| 6206 | - cnt += 100; | |
| 6220 | + cnt += isMonospace ? monoAvg : stdAvg; | |
| 6207 | 6221 | continue; |
| 6208 | 6222 | } |
| 6209 | - if( c>=0x20 && c<=0x7e ){ | |
| 6223 | + if( isMonospace ){ | |
| 6224 | + cnt += monoAvg; | |
| 6225 | + }else if( c >= 0x20 && c <= 0x7e ){ | |
| 6210 | 6226 | cnt += awChar[c-0x20]; |
| 6211 | 6227 | }else{ |
| 6212 | - cnt += 100; | |
| 6228 | + cnt += stdAvg; | |
| 6213 | 6229 | } |
| 6214 | 6230 | } |
| 6215 | 6231 | return cnt; |
| 6216 | 6232 | } |
| 6217 | 6233 | |
| @@ -7195,10 +7211,12 @@ | ||
| 7195 | 7211 | { "last", 4, T_LAST, 0, 0 }, |
| 7196 | 7212 | { "left", 4, T_LEFT, DIR_LEFT, CP_W }, |
| 7197 | 7213 | { "ljust", 5, T_LJUST, 0, 0 }, |
| 7198 | 7214 | { "max", 3, T_FUNC2, FN_MAX, 0 }, |
| 7199 | 7215 | { "min", 3, T_FUNC2, FN_MIN, 0 }, |
| 7216 | + { "mono", 4, T_MONO, 0, 0 }, | |
| 7217 | + { "monospace", 9, T_MONO, 0, 0 }, | |
| 7200 | 7218 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7201 | 7219 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7202 | 7220 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7203 | 7221 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7204 | 7222 | { "of", 2, T_OF, 0, 0 }, |
| @@ -8122,6 +8140,6 @@ | ||
| 8122 | 8140 | |
| 8123 | 8141 | |
| 8124 | 8142 | #endif /* PIKCHR_TCL */ |
| 8125 | 8143 | |
| 8126 | 8144 | |
| 8127 | -#line 8152 "pikchr.c" | |
| 8145 | +#line 8170 "pikchr.c" | |
| 8128 | 8146 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -59,11 +59,11 @@ | |
| 59 | ** cc pikchr.c -o pikchr.o |
| 60 | ** |
| 61 | ** Add -DPIKCHR_SHELL to add a main() routine that reads input files |
| 62 | ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for |
| 63 | ** -fsanitizer=fuzzer testing. |
| 64 | ** |
| 65 | **************************************************************************** |
| 66 | ** IMPLEMENTATION NOTES (for people who want to understand the internal |
| 67 | ** operation of this software, perhaps to extend the code or to fix bugs): |
| 68 | ** |
| 69 | ** Each call to pikchr() uses a single instance of the Pik structure to |
| @@ -203,12 +203,13 @@ | |
| 203 | #define TP_SMALL 0x0200 /* Smaller font */ |
| 204 | #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ |
| 205 | #define TP_SZMASK 0x0700 /* Font size mask */ |
| 206 | #define TP_ITALIC 0x1000 /* Italic font */ |
| 207 | #define TP_BOLD 0x2000 /* Bold font */ |
| 208 | #define TP_FMASK 0x3000 /* Mask for font style */ |
| 209 | #define TP_ALIGN 0x4000 /* Rotate to align with the line */ |
| 210 | |
| 211 | /* An object to hold a position in 2-D space */ |
| 212 | struct PPoint { |
| 213 | PNum x, y; /* X and Y coordinates */ |
| 214 | }; |
| @@ -470,11 +471,11 @@ | |
| 470 | static void pik_bbox_init(PBox*); |
| 471 | static void pik_bbox_addbox(PBox*,PBox*); |
| 472 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 473 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 474 | static void pik_add_txt(Pik*,PToken*,int); |
| 475 | static int pik_text_length(const PToken *pToken); |
| 476 | static void pik_size_to_fit(Pik*,PToken*,int); |
| 477 | static int pik_text_position(int,PToken*); |
| 478 | static PNum pik_property_of(PObj*,PToken*); |
| 479 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 480 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| @@ -489,11 +490,11 @@ | |
| 489 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 490 | static PNum pik_dist(PPoint*,PPoint*); |
| 491 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 492 | |
| 493 | |
| 494 | #line 520 "pikchr.c" |
| 495 | /**************** End of %include directives **********************************/ |
| 496 | /* These constants specify the various numeric values for terminal symbols. |
| 497 | ***************** Begin token definitions *************************************/ |
| 498 | #ifndef T_ID |
| 499 | #define T_ID 1 |
| @@ -564,38 +565,39 @@ | |
| 564 | #define T_RJUST 66 |
| 565 | #define T_ABOVE 67 |
| 566 | #define T_BELOW 68 |
| 567 | #define T_ITALIC 69 |
| 568 | #define T_BOLD 70 |
| 569 | #define T_ALIGNED 71 |
| 570 | #define T_BIG 72 |
| 571 | #define T_SMALL 73 |
| 572 | #define T_AND 74 |
| 573 | #define T_LT 75 |
| 574 | #define T_GT 76 |
| 575 | #define T_ON 77 |
| 576 | #define T_WAY 78 |
| 577 | #define T_BETWEEN 79 |
| 578 | #define T_THE 80 |
| 579 | #define T_NTH 81 |
| 580 | #define T_VERTEX 82 |
| 581 | #define T_TOP 83 |
| 582 | #define T_BOTTOM 84 |
| 583 | #define T_START 85 |
| 584 | #define T_END 86 |
| 585 | #define T_IN 87 |
| 586 | #define T_THIS 88 |
| 587 | #define T_DOT_U 89 |
| 588 | #define T_LAST 90 |
| 589 | #define T_NUMBER 91 |
| 590 | #define T_FUNC1 92 |
| 591 | #define T_FUNC2 93 |
| 592 | #define T_DIST 94 |
| 593 | #define T_DOT_XY 95 |
| 594 | #define T_X 96 |
| 595 | #define T_Y 97 |
| 596 | #define T_DOT_L 98 |
| 597 | #endif |
| 598 | /**************** End token definitions ***************************************/ |
| 599 | |
| 600 | /* The next sections is a series of control #defines. |
| 601 | ** various aspects of the generated parser. |
| @@ -651,22 +653,22 @@ | |
| 651 | #ifndef INTERFACE |
| 652 | # define INTERFACE 1 |
| 653 | #endif |
| 654 | /************* Begin control #defines *****************************************/ |
| 655 | #define YYCODETYPE unsigned char |
| 656 | #define YYNOCODE 135 |
| 657 | #define YYACTIONTYPE unsigned short int |
| 658 | #define pik_parserTOKENTYPE PToken |
| 659 | typedef union { |
| 660 | int yyinit; |
| 661 | pik_parserTOKENTYPE yy0; |
| 662 | PRel yy10; |
| 663 | PObj* yy36; |
| 664 | PPoint yy79; |
| 665 | PNum yy153; |
| 666 | short int yy164; |
| 667 | PList* yy227; |
| 668 | } YYMINORTYPE; |
| 669 | #ifndef YYSTACKDEPTH |
| 670 | #define YYSTACKDEPTH 100 |
| 671 | #endif |
| 672 | #define pik_parserARG_SDECL |
| @@ -681,11 +683,11 @@ | |
| 681 | #define pik_parserCTX_STORE yypParser->p=p; |
| 682 | #define YYFALLBACK 1 |
| 683 | #define YYNSTATE 164 |
| 684 | #define YYNRULE 156 |
| 685 | #define YYNRULE_WITH_ACTION 116 |
| 686 | #define YYNTOKEN 99 |
| 687 | #define YY_MAX_SHIFT 163 |
| 688 | #define YY_MIN_SHIFTREDUCE 287 |
| 689 | #define YY_MAX_SHIFTREDUCE 442 |
| 690 | #define YY_ERROR_ACTION 443 |
| 691 | #define YY_ACCEPT_ACTION 444 |
| @@ -756,322 +758,324 @@ | |
| 756 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 757 | ** shifting non-terminals after a reduce. |
| 758 | ** yy_default[] Default action for each state. |
| 759 | ** |
| 760 | *********** Begin parsing tables **********************************************/ |
| 761 | #define YY_ACTTAB_COUNT (1303) |
| 762 | static const YYACTIONTYPE yy_action[] = { |
| 763 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 764 | /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, |
| 765 | /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, |
| 766 | /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, |
| 767 | /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, |
| 768 | /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, |
| 769 | /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, |
| 770 | /* 70 */ 577, 73, 376, 148, 474, 533, 161, 119, 112, 113, |
| 771 | /* 80 */ 120, 161, 119, 128, 427, 428, 339, 357, 81, 531, |
| 772 | /* 90 */ 161, 119, 474, 36, 330, 13, 306, 322, 323, 9, |
| 773 | /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, |
| 774 | /* 110 */ 579, 310, 31, 339, 339, 339, 339, 425, 426, 340, |
| 775 | /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, |
| 776 | /* 130 */ 46, 59, 60, 64, 63, 62, 61, 54, 51, 376, |
| 777 | /* 140 */ 69, 108, 2, 47, 403, 83, 297, 435, 375, 84, |
| 778 | /* 150 */ 117, 80, 35, 308, 79, 133, 122, 126, 441, 440, |
| 779 | /* 160 */ 299, 123, 3, 404, 405, 406, 408, 80, 298, 308, |
| 780 | /* 170 */ 79, 4, 411, 412, 413, 414, 441, 440, 350, 350, |
| 781 | /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 62, 61, |
| 782 | /* 190 */ 67, 434, 1, 75, 378, 158, 74, 76, 148, 411, |
| 783 | /* 200 */ 412, 413, 414, 124, 113, 120, 161, 119, 106, 434, |
| 784 | /* 210 */ 436, 437, 438, 439, 5, 375, 6, 117, 393, 155, |
| 785 | /* 220 */ 154, 153, 394, 435, 69, 59, 60, 149, 436, 437, |
| 786 | /* 230 */ 438, 439, 535, 376, 398, 399, 2, 424, 427, 428, |
| 787 | /* 240 */ 339, 156, 156, 156, 423, 394, 435, 65, 59, 60, |
| 788 | /* 250 */ 162, 131, 441, 440, 397, 72, 376, 148, 118, 2, |
| 789 | /* 260 */ 380, 157, 125, 113, 120, 161, 119, 339, 339, 339, |
| 790 | /* 270 */ 339, 425, 426, 535, 11, 441, 440, 394, 356, 535, |
| 791 | /* 280 */ 59, 60, 535, 379, 159, 434, 149, 12, 102, 446, |
| 792 | /* 290 */ 432, 42, 138, 14, 435, 139, 301, 302, 303, 36, |
| 793 | /* 300 */ 305, 430, 106, 16, 436, 437, 438, 439, 434, 375, |
| 794 | /* 310 */ 18, 117, 393, 155, 154, 153, 44, 142, 140, 64, |
| 795 | /* 320 */ 63, 62, 61, 441, 440, 106, 19, 436, 437, 438, |
| 796 | /* 330 */ 439, 45, 375, 20, 117, 393, 155, 154, 153, 68, |
| 797 | /* 340 */ 55, 114, 64, 63, 62, 61, 147, 146, 394, 473, |
| 798 | /* 350 */ 359, 59, 60, 43, 23, 391, 434, 106, 26, 376, |
| 799 | /* 360 */ 57, 58, 42, 49, 375, 392, 117, 393, 155, 154, |
| 800 | /* 370 */ 153, 64, 63, 62, 61, 436, 437, 438, 439, 384, |
| 801 | /* 380 */ 382, 383, 22, 21, 377, 473, 160, 70, 39, 445, |
| 802 | /* 390 */ 24, 445, 145, 141, 431, 142, 140, 64, 63, 62, |
| 803 | /* 400 */ 61, 394, 15, 445, 59, 60, 64, 63, 62, 61, |
| 804 | /* 410 */ 391, 445, 376, 445, 445, 42, 445, 445, 55, 391, |
| 805 | /* 420 */ 156, 156, 156, 445, 147, 146, 445, 52, 106, 445, |
| 806 | /* 430 */ 445, 43, 445, 445, 445, 375, 445, 117, 393, 155, |
| 807 | /* 440 */ 154, 153, 445, 394, 143, 445, 59, 60, 64, 63, |
| 808 | /* 450 */ 62, 61, 313, 445, 376, 378, 158, 42, 445, 445, |
| 809 | /* 460 */ 22, 21, 121, 447, 454, 29, 445, 445, 24, 450, |
| 810 | /* 470 */ 145, 141, 431, 142, 140, 64, 63, 62, 61, 445, |
| 811 | /* 480 */ 163, 106, 445, 445, 444, 27, 445, 445, 375, 445, |
| 812 | /* 490 */ 117, 393, 155, 154, 153, 445, 55, 74, 445, 148, |
| 813 | /* 500 */ 445, 445, 147, 146, 497, 113, 120, 161, 119, 43, |
| 814 | /* 510 */ 445, 394, 445, 445, 59, 60, 445, 445, 445, 118, |
| 815 | /* 520 */ 445, 445, 376, 106, 445, 42, 445, 445, 149, 445, |
| 816 | /* 530 */ 375, 445, 117, 393, 155, 154, 153, 445, 22, 21, |
| 817 | /* 540 */ 394, 144, 445, 59, 60, 445, 24, 445, 145, 141, |
| 818 | /* 550 */ 431, 376, 445, 445, 42, 445, 132, 130, 394, 445, |
| 819 | /* 560 */ 445, 59, 60, 109, 447, 454, 29, 445, 445, 376, |
| 820 | /* 570 */ 450, 445, 42, 445, 394, 445, 445, 59, 60, 445, |
| 821 | /* 580 */ 445, 163, 445, 445, 445, 102, 27, 445, 42, 445, |
| 822 | /* 590 */ 445, 106, 445, 64, 63, 62, 61, 445, 375, 445, |
| 823 | /* 600 */ 117, 393, 155, 154, 153, 394, 355, 445, 59, 60, |
| 824 | /* 610 */ 445, 445, 445, 445, 445, 74, 376, 148, 445, 40, |
| 825 | /* 620 */ 106, 445, 496, 113, 120, 161, 119, 375, 445, 117, |
| 826 | /* 630 */ 393, 155, 154, 153, 445, 448, 454, 29, 106, 445, |
| 827 | /* 640 */ 445, 450, 445, 445, 445, 375, 149, 117, 393, 155, |
| 828 | /* 650 */ 154, 153, 163, 445, 106, 445, 445, 27, 445, 445, |
| 829 | /* 660 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, |
| 830 | /* 670 */ 445, 59, 60, 64, 63, 62, 61, 445, 445, 376, |
| 831 | /* 680 */ 445, 445, 41, 445, 445, 106, 354, 64, 63, 62, |
| 832 | /* 690 */ 61, 445, 375, 445, 117, 393, 155, 154, 153, 445, |
| 833 | /* 700 */ 445, 445, 74, 445, 148, 445, 88, 445, 445, 490, |
| 834 | /* 710 */ 113, 120, 161, 119, 445, 120, 161, 119, 17, 74, |
| 835 | /* 720 */ 445, 148, 110, 110, 445, 445, 484, 113, 120, 161, |
| 836 | /* 730 */ 119, 445, 445, 149, 74, 445, 148, 152, 445, 445, |
| 837 | /* 740 */ 445, 483, 113, 120, 161, 119, 445, 445, 106, 445, |
| 838 | /* 750 */ 149, 445, 445, 107, 445, 375, 445, 117, 393, 155, |
| 839 | /* 760 */ 154, 153, 120, 161, 119, 149, 478, 74, 445, 148, |
| 840 | /* 770 */ 445, 88, 445, 445, 480, 113, 120, 161, 119, 445, |
| 841 | /* 780 */ 120, 161, 119, 74, 152, 148, 10, 479, 479, 445, |
| 842 | /* 790 */ 134, 113, 120, 161, 119, 445, 445, 445, 149, 74, |
| 843 | /* 800 */ 445, 148, 152, 445, 445, 445, 517, 113, 120, 161, |
| 844 | /* 810 */ 119, 445, 445, 74, 149, 148, 445, 445, 445, 445, |
| 845 | /* 820 */ 137, 113, 120, 161, 119, 74, 445, 148, 445, 445, |
| 846 | /* 830 */ 149, 445, 525, 113, 120, 161, 119, 445, 74, 445, |
| 847 | /* 840 */ 148, 445, 445, 445, 149, 527, 113, 120, 161, 119, |
| 848 | /* 850 */ 445, 445, 74, 445, 148, 445, 149, 445, 445, 524, |
| 849 | /* 860 */ 113, 120, 161, 119, 74, 445, 148, 445, 445, 149, |
| 850 | /* 870 */ 445, 526, 113, 120, 161, 119, 445, 445, 74, 445, |
| 851 | /* 880 */ 148, 445, 88, 149, 445, 523, 113, 120, 161, 119, |
| 852 | /* 890 */ 445, 120, 161, 119, 74, 149, 148, 85, 111, 111, |
| 853 | /* 900 */ 445, 522, 113, 120, 161, 119, 120, 161, 119, 149, |
| 854 | /* 910 */ 74, 445, 148, 152, 445, 445, 445, 521, 113, 120, |
| 855 | /* 920 */ 161, 119, 445, 445, 74, 149, 148, 445, 152, 445, |
| 856 | /* 930 */ 445, 520, 113, 120, 161, 119, 74, 445, 148, 445, |
| 857 | /* 940 */ 445, 149, 445, 519, 113, 120, 161, 119, 445, 74, |
| 858 | /* 950 */ 445, 148, 445, 445, 445, 149, 150, 113, 120, 161, |
| 859 | /* 960 */ 119, 445, 445, 74, 445, 148, 445, 149, 445, 445, |
| 860 | /* 970 */ 151, 113, 120, 161, 119, 74, 445, 148, 445, 445, |
| 861 | /* 980 */ 149, 445, 136, 113, 120, 161, 119, 445, 445, 74, |
| 862 | /* 990 */ 445, 148, 107, 445, 149, 445, 135, 113, 120, 161, |
| 863 | /* 1000 */ 119, 120, 161, 119, 445, 463, 149, 445, 88, 445, |
| 864 | /* 1010 */ 445, 445, 78, 78, 445, 445, 107, 120, 161, 119, |
| 865 | /* 1020 */ 149, 445, 445, 152, 82, 120, 161, 119, 445, 463, |
| 866 | /* 1030 */ 445, 466, 86, 34, 445, 88, 445, 569, 445, 152, |
| 867 | /* 1040 */ 445, 120, 161, 119, 120, 161, 119, 152, 107, 445, |
| 868 | /* 1050 */ 445, 475, 64, 63, 62, 61, 445, 120, 161, 119, |
| 869 | /* 1060 */ 98, 451, 445, 152, 89, 396, 152, 90, 445, 120, |
| 870 | /* 1070 */ 161, 119, 445, 120, 161, 119, 120, 161, 119, 152, |
| 871 | /* 1080 */ 445, 64, 63, 62, 61, 445, 445, 445, 445, 445, |
| 872 | /* 1090 */ 87, 152, 445, 99, 395, 152, 100, 445, 152, 120, |
| 873 | /* 1100 */ 161, 119, 120, 161, 119, 120, 161, 119, 445, 101, |
| 874 | /* 1110 */ 64, 63, 62, 61, 445, 445, 445, 445, 120, 161, |
| 875 | /* 1120 */ 119, 152, 91, 391, 152, 445, 445, 152, 103, 445, |
| 876 | /* 1130 */ 445, 120, 161, 119, 445, 92, 445, 120, 161, 119, |
| 877 | /* 1140 */ 152, 93, 445, 445, 120, 161, 119, 104, 445, 445, |
| 878 | /* 1150 */ 120, 161, 119, 152, 445, 445, 120, 161, 119, 152, |
| 879 | /* 1160 */ 445, 445, 445, 445, 94, 445, 152, 445, 445, 445, |
| 880 | /* 1170 */ 105, 445, 152, 120, 161, 119, 445, 95, 152, 120, |
| 881 | /* 1180 */ 161, 119, 96, 445, 445, 445, 120, 161, 119, 445, |
| 882 | /* 1190 */ 445, 120, 161, 119, 97, 152, 445, 445, 445, 445, |
| 883 | /* 1200 */ 549, 152, 445, 120, 161, 119, 548, 445, 152, 120, |
| 884 | /* 1210 */ 161, 119, 445, 152, 445, 120, 161, 119, 445, 445, |
| 885 | /* 1220 */ 445, 445, 445, 547, 445, 152, 445, 445, 445, 445, |
| 886 | /* 1230 */ 445, 152, 120, 161, 119, 546, 445, 152, 445, 115, |
| 887 | /* 1240 */ 445, 445, 116, 445, 120, 161, 119, 445, 120, 161, |
| 888 | /* 1250 */ 119, 120, 161, 119, 152, 64, 63, 62, 61, 64, |
| 889 | /* 1260 */ 63, 62, 61, 445, 445, 445, 152, 445, 445, 445, |
| 890 | /* 1270 */ 152, 445, 445, 152, 445, 445, 50, 445, 445, 445, |
| 891 | /* 1280 */ 53, 64, 63, 62, 61, 445, 445, 445, 445, 445, |
| 892 | /* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 893 | /* 1300 */ 445, 445, 56, |
| 894 | }; |
| 895 | static const YYCODETYPE yy_lookahead[] = { |
| 896 | /* 0 */ 0, 112, 113, 114, 133, 101, 102, 103, 105, 105, |
| 897 | /* 10 */ 10, 112, 113, 114, 110, 111, 112, 113, 114, 105, |
| 898 | /* 20 */ 20, 21, 22, 104, 24, 125, 107, 108, 28, 4, |
| 899 | /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 134, 39, |
| 900 | /* 40 */ 40, 41, 42, 104, 44, 45, 107, 108, 106, 49, |
| 901 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 902 | /* 60 */ 60, 61, 62, 63, 0, 112, 113, 114, 129, 130, |
| 903 | /* 70 */ 131, 103, 12, 105, 10, 112, 113, 114, 110, 111, |
| 904 | /* 80 */ 112, 113, 114, 105, 20, 21, 22, 17, 24, 112, |
| 905 | /* 90 */ 113, 114, 28, 10, 2, 25, 25, 33, 34, 35, |
| 906 | /* 100 */ 36, 37, 134, 39, 40, 41, 42, 2, 44, 45, |
| 907 | /* 110 */ 132, 28, 127, 49, 50, 51, 52, 53, 54, 55, |
| 908 | /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, |
| 909 | /* 130 */ 38, 4, 5, 4, 5, 6, 7, 4, 5, 12, |
| 910 | /* 140 */ 3, 81, 15, 38, 1, 115, 17, 2, 88, 115, |
| 911 | /* 150 */ 90, 24, 128, 26, 27, 12, 1, 14, 31, 32, |
| 912 | /* 160 */ 19, 18, 16, 20, 21, 22, 23, 24, 17, 26, |
| 913 | /* 170 */ 27, 15, 29, 30, 31, 32, 31, 32, 64, 65, |
| 914 | /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 6, 7, |
| 915 | /* 190 */ 43, 64, 13, 48, 26, 27, 103, 48, 105, 29, |
| 916 | /* 200 */ 30, 31, 32, 110, 111, 112, 113, 114, 81, 64, |
| 917 | /* 210 */ 83, 84, 85, 86, 40, 88, 40, 90, 91, 92, |
| 918 | /* 220 */ 93, 94, 1, 2, 87, 4, 5, 134, 83, 84, |
| 919 | /* 230 */ 85, 86, 48, 12, 96, 97, 15, 41, 20, 21, |
| 920 | /* 240 */ 22, 20, 21, 22, 41, 1, 2, 98, 4, 5, |
| 921 | /* 250 */ 82, 47, 31, 32, 17, 103, 12, 105, 90, 15, |
| 922 | /* 260 */ 26, 27, 110, 111, 112, 113, 114, 49, 50, 51, |
| 923 | /* 270 */ 52, 53, 54, 89, 25, 31, 32, 1, 17, 95, |
| 924 | /* 280 */ 4, 5, 98, 26, 27, 64, 134, 74, 12, 0, |
| 925 | /* 290 */ 79, 15, 78, 3, 2, 80, 20, 21, 22, 10, |
| 926 | /* 300 */ 24, 79, 81, 3, 83, 84, 85, 86, 64, 88, |
| 927 | /* 310 */ 3, 90, 91, 92, 93, 94, 38, 2, 3, 4, |
| 928 | /* 320 */ 5, 6, 7, 31, 32, 81, 3, 83, 84, 85, |
| 929 | /* 330 */ 86, 16, 88, 3, 90, 91, 92, 93, 94, 3, |
| 930 | /* 340 */ 25, 95, 4, 5, 6, 7, 31, 32, 1, 2, |
| 931 | /* 350 */ 76, 4, 5, 38, 25, 17, 64, 81, 15, 12, |
| 932 | /* 360 */ 15, 15, 15, 25, 88, 17, 90, 91, 92, 93, |
| 933 | /* 370 */ 94, 4, 5, 6, 7, 83, 84, 85, 86, 28, |
| 934 | /* 380 */ 28, 28, 67, 68, 12, 38, 89, 3, 11, 135, |
| 935 | /* 390 */ 75, 135, 77, 78, 79, 2, 3, 4, 5, 6, |
| 936 | /* 400 */ 7, 1, 35, 135, 4, 5, 4, 5, 6, 7, |
| 937 | /* 410 */ 17, 135, 12, 135, 135, 15, 135, 135, 25, 17, |
| 938 | /* 420 */ 20, 21, 22, 135, 31, 32, 135, 25, 81, 135, |
| 939 | /* 430 */ 135, 38, 135, 135, 135, 88, 135, 90, 91, 92, |
| 940 | /* 440 */ 93, 94, 135, 1, 2, 135, 4, 5, 4, 5, |
| 941 | /* 450 */ 6, 7, 8, 135, 12, 26, 27, 15, 135, 135, |
| 942 | /* 460 */ 67, 68, 99, 100, 101, 102, 135, 135, 75, 106, |
| 943 | /* 470 */ 77, 78, 79, 2, 3, 4, 5, 6, 7, 135, |
| 944 | /* 480 */ 117, 81, 135, 135, 121, 122, 135, 135, 88, 135, |
| 945 | /* 490 */ 90, 91, 92, 93, 94, 135, 25, 103, 135, 105, |
| 946 | /* 500 */ 135, 135, 31, 32, 110, 111, 112, 113, 114, 38, |
| 947 | /* 510 */ 135, 1, 135, 135, 4, 5, 135, 135, 135, 90, |
| 948 | /* 520 */ 135, 135, 12, 81, 135, 15, 135, 135, 134, 135, |
| 949 | /* 530 */ 88, 135, 90, 91, 92, 93, 94, 135, 67, 68, |
| 950 | /* 540 */ 1, 2, 135, 4, 5, 135, 75, 135, 77, 78, |
| 951 | /* 550 */ 79, 12, 135, 135, 15, 135, 46, 47, 1, 135, |
| 952 | /* 560 */ 135, 4, 5, 99, 100, 101, 102, 135, 135, 12, |
| 953 | /* 570 */ 106, 135, 15, 135, 1, 135, 135, 4, 5, 135, |
| 954 | /* 580 */ 135, 117, 135, 135, 135, 12, 122, 135, 15, 135, |
| 955 | /* 590 */ 135, 81, 135, 4, 5, 6, 7, 135, 88, 135, |
| 956 | /* 600 */ 90, 91, 92, 93, 94, 1, 17, 135, 4, 5, |
| 957 | /* 610 */ 135, 135, 135, 135, 135, 103, 12, 105, 135, 15, |
| 958 | /* 620 */ 81, 135, 110, 111, 112, 113, 114, 88, 135, 90, |
| 959 | /* 630 */ 91, 92, 93, 94, 135, 100, 101, 102, 81, 135, |
| 960 | /* 640 */ 135, 106, 135, 135, 135, 88, 134, 90, 91, 92, |
| 961 | /* 650 */ 93, 94, 117, 135, 81, 135, 135, 122, 135, 135, |
| 962 | /* 660 */ 135, 88, 135, 90, 91, 92, 93, 94, 1, 135, |
| 963 | /* 670 */ 135, 4, 5, 4, 5, 6, 7, 135, 135, 12, |
| 964 | /* 680 */ 135, 135, 15, 135, 135, 81, 17, 4, 5, 6, |
| 965 | /* 690 */ 7, 135, 88, 135, 90, 91, 92, 93, 94, 135, |
| 966 | /* 700 */ 135, 135, 103, 135, 105, 135, 103, 135, 135, 110, |
| 967 | /* 710 */ 111, 112, 113, 114, 135, 112, 113, 114, 35, 103, |
| 968 | /* 720 */ 135, 105, 119, 120, 135, 135, 110, 111, 112, 113, |
| 969 | /* 730 */ 114, 135, 135, 134, 103, 135, 105, 134, 135, 135, |
| 970 | /* 740 */ 135, 110, 111, 112, 113, 114, 135, 135, 81, 135, |
| 971 | /* 750 */ 134, 135, 135, 103, 135, 88, 135, 90, 91, 92, |
| 972 | /* 760 */ 93, 94, 112, 113, 114, 134, 116, 103, 135, 105, |
| 973 | /* 770 */ 135, 103, 135, 135, 110, 111, 112, 113, 114, 135, |
| 974 | /* 780 */ 112, 113, 114, 103, 134, 105, 118, 119, 120, 135, |
| 975 | /* 790 */ 110, 111, 112, 113, 114, 135, 135, 135, 134, 103, |
| 976 | /* 800 */ 135, 105, 134, 135, 135, 135, 110, 111, 112, 113, |
| 977 | /* 810 */ 114, 135, 135, 103, 134, 105, 135, 135, 135, 135, |
| 978 | /* 820 */ 110, 111, 112, 113, 114, 103, 135, 105, 135, 135, |
| 979 | /* 830 */ 134, 135, 110, 111, 112, 113, 114, 135, 103, 135, |
| 980 | /* 840 */ 105, 135, 135, 135, 134, 110, 111, 112, 113, 114, |
| 981 | /* 850 */ 135, 135, 103, 135, 105, 135, 134, 135, 135, 110, |
| 982 | /* 860 */ 111, 112, 113, 114, 103, 135, 105, 135, 135, 134, |
| 983 | /* 870 */ 135, 110, 111, 112, 113, 114, 135, 135, 103, 135, |
| 984 | /* 880 */ 105, 135, 103, 134, 135, 110, 111, 112, 113, 114, |
| 985 | /* 890 */ 135, 112, 113, 114, 103, 134, 105, 103, 119, 120, |
| 986 | /* 900 */ 135, 110, 111, 112, 113, 114, 112, 113, 114, 134, |
| 987 | /* 910 */ 103, 135, 105, 134, 135, 135, 135, 110, 111, 112, |
| 988 | /* 920 */ 113, 114, 135, 135, 103, 134, 105, 135, 134, 135, |
| 989 | /* 930 */ 135, 110, 111, 112, 113, 114, 103, 135, 105, 135, |
| 990 | /* 940 */ 135, 134, 135, 110, 111, 112, 113, 114, 135, 103, |
| 991 | /* 950 */ 135, 105, 135, 135, 135, 134, 110, 111, 112, 113, |
| 992 | /* 960 */ 114, 135, 135, 103, 135, 105, 135, 134, 135, 135, |
| 993 | /* 970 */ 110, 111, 112, 113, 114, 103, 135, 105, 135, 135, |
| 994 | /* 980 */ 134, 135, 110, 111, 112, 113, 114, 135, 135, 103, |
| 995 | /* 990 */ 135, 105, 103, 135, 134, 135, 110, 111, 112, 113, |
| 996 | /* 1000 */ 114, 112, 113, 114, 135, 116, 134, 135, 103, 135, |
| 997 | /* 1010 */ 135, 135, 123, 124, 135, 135, 103, 112, 113, 114, |
| 998 | /* 1020 */ 134, 135, 135, 134, 119, 112, 113, 114, 135, 116, |
| 999 | /* 1030 */ 135, 126, 103, 128, 135, 103, 135, 124, 135, 134, |
| 1000 | /* 1040 */ 135, 112, 113, 114, 112, 113, 114, 134, 103, 135, |
| 1001 | /* 1050 */ 135, 119, 4, 5, 6, 7, 135, 112, 113, 114, |
| 1002 | /* 1060 */ 103, 116, 135, 134, 103, 17, 134, 103, 135, 112, |
| 1003 | /* 1070 */ 113, 114, 135, 112, 113, 114, 112, 113, 114, 134, |
| 1004 | /* 1080 */ 135, 4, 5, 6, 7, 135, 135, 135, 135, 135, |
| 1005 | /* 1090 */ 103, 134, 135, 103, 17, 134, 103, 135, 134, 112, |
| 1006 | /* 1100 */ 113, 114, 112, 113, 114, 112, 113, 114, 135, 103, |
| 1007 | /* 1110 */ 4, 5, 6, 7, 135, 135, 135, 135, 112, 113, |
| 1008 | /* 1120 */ 114, 134, 103, 17, 134, 135, 135, 134, 103, 135, |
| 1009 | /* 1130 */ 135, 112, 113, 114, 135, 103, 135, 112, 113, 114, |
| 1010 | /* 1140 */ 134, 103, 135, 135, 112, 113, 114, 103, 135, 135, |
| 1011 | /* 1150 */ 112, 113, 114, 134, 135, 135, 112, 113, 114, 134, |
| 1012 | /* 1160 */ 135, 135, 135, 135, 103, 135, 134, 135, 135, 135, |
| 1013 | /* 1170 */ 103, 135, 134, 112, 113, 114, 135, 103, 134, 112, |
| 1014 | /* 1180 */ 113, 114, 103, 135, 135, 135, 112, 113, 114, 135, |
| 1015 | /* 1190 */ 135, 112, 113, 114, 103, 134, 135, 135, 135, 135, |
| 1016 | /* 1200 */ 103, 134, 135, 112, 113, 114, 103, 135, 134, 112, |
| 1017 | /* 1210 */ 113, 114, 135, 134, 135, 112, 113, 114, 135, 135, |
| 1018 | /* 1220 */ 135, 135, 135, 103, 135, 134, 135, 135, 135, 135, |
| 1019 | /* 1230 */ 135, 134, 112, 113, 114, 103, 135, 134, 135, 103, |
| 1020 | /* 1240 */ 135, 135, 103, 135, 112, 113, 114, 135, 112, 113, |
| 1021 | /* 1250 */ 114, 112, 113, 114, 134, 4, 5, 6, 7, 4, |
| 1022 | /* 1260 */ 5, 6, 7, 135, 135, 135, 134, 135, 135, 135, |
| 1023 | /* 1270 */ 134, 135, 135, 134, 135, 135, 25, 135, 135, 135, |
| 1024 | /* 1280 */ 25, 4, 5, 6, 7, 135, 135, 135, 135, 135, |
| 1025 | /* 1290 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1026 | /* 1300 */ 135, 135, 25, 135, 135, 135, 135, 135, 135, 135, |
| 1027 | /* 1310 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1028 | /* 1320 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1029 | /* 1330 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1030 | /* 1340 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1031 | /* 1350 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1032 | /* 1360 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1033 | /* 1370 */ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, |
| 1034 | /* 1380 */ 135, 99, 99, 99, 99, 99, 99, 99, 99, 99, |
| 1035 | /* 1390 */ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, |
| 1036 | /* 1400 */ 99, 99, |
| 1037 | }; |
| 1038 | #define YY_SHIFT_COUNT (163) |
| 1039 | #define YY_SHIFT_MIN (0) |
| 1040 | #define YY_SHIFT_MAX (1277) |
| 1041 | static const unsigned short int yy_shift_ofst[] = { |
| 1042 | /* 0 */ 143, 127, 221, 244, 244, 244, 244, 244, 244, 244, |
| 1043 | /* 10 */ 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, |
| 1044 | /* 20 */ 244, 244, 244, 244, 244, 244, 244, 276, 510, 557, |
| 1045 | /* 30 */ 276, 143, 347, 347, 0, 64, 143, 573, 557, 573, |
| 1046 | /* 40 */ 400, 400, 400, 442, 539, 557, 557, 557, 557, 557, |
| 1047 | /* 50 */ 557, 604, 557, 557, 667, 557, 557, 557, 557, 557, |
| 1048 | /* 60 */ 557, 557, 557, 557, 557, 218, 60, 60, 60, 60, |
| 1049 | /* 70 */ 60, 145, 315, 393, 471, 292, 292, 170, 71, 1303, |
| 1050 | /* 80 */ 1303, 1303, 1303, 114, 114, 338, 402, 129, 444, 367, |
| 1051 | /* 90 */ 683, 589, 1251, 669, 1255, 1048, 1277, 1077, 1106, 25, |
| 1052 | /* 100 */ 25, 25, 184, 25, 25, 25, 168, 25, 429, 83, |
| 1053 | /* 110 */ 92, 105, 70, 133, 138, 182, 182, 234, 257, 137, |
| 1054 | /* 120 */ 149, 289, 141, 155, 151, 146, 156, 147, 174, 176, |
| 1055 | /* 130 */ 196, 203, 204, 179, 237, 249, 213, 261, 211, 214, |
| 1056 | /* 140 */ 215, 222, 290, 300, 307, 278, 323, 330, 336, 246, |
| 1057 | /* 150 */ 274, 329, 246, 343, 345, 346, 348, 351, 352, 353, |
| 1058 | /* 160 */ 372, 297, 384, 377, |
| 1059 | }; |
| 1060 | #define YY_REDUCE_COUNT (82) |
| 1061 | #define YY_REDUCE_MIN (-129) |
| 1062 | #define YY_REDUCE_MAX (1139) |
| 1063 | static const short yy_reduce_ofst[] = { |
| 1064 | /* 0 */ 363, -96, -32, 93, 152, 394, 512, 599, 616, 631, |
| 1065 | /* 10 */ 664, 680, 696, 710, 722, 735, 749, 761, 775, 791, |
| 1066 | /* 20 */ 807, 821, 833, 846, 860, 872, 886, 889, 668, 905, |
| 1067 | /* 30 */ 913, 464, 603, 779, -61, -61, 535, 650, 932, 945, |
| 1068 | /* 40 */ 794, 929, 957, 961, 964, 987, 990, 993, 1006, 1019, |
| 1069 | /* 50 */ 1025, 1032, 1038, 1044, 1061, 1067, 1074, 1079, 1091, 1097, |
| 1070 | /* 60 */ 1103, 1120, 1132, 1136, 1139, -81, -111, -101, -47, -37, |
| 1071 | /* 70 */ -23, -22, -129, -129, -129, -97, -86, -58, -100, -15, |
| 1072 | /* 80 */ 30, 34, 24, |
| 1073 | }; |
| 1074 | static const YYACTIONTYPE yy_default[] = { |
| 1075 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1076 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1077 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1177,10 +1181,11 @@ | |
| 1177 | 0, /* RJUST => nothing */ |
| 1178 | 0, /* ABOVE => nothing */ |
| 1179 | 0, /* BELOW => nothing */ |
| 1180 | 0, /* ITALIC => nothing */ |
| 1181 | 0, /* BOLD => nothing */ |
| 1182 | 0, /* ALIGNED => nothing */ |
| 1183 | 0, /* BIG => nothing */ |
| 1184 | 0, /* SMALL => nothing */ |
| 1185 | 0, /* AND => nothing */ |
| 1186 | 0, /* LT => nothing */ |
| @@ -1364,74 +1369,75 @@ | |
| 1364 | /* 66 */ "RJUST", |
| 1365 | /* 67 */ "ABOVE", |
| 1366 | /* 68 */ "BELOW", |
| 1367 | /* 69 */ "ITALIC", |
| 1368 | /* 70 */ "BOLD", |
| 1369 | /* 71 */ "ALIGNED", |
| 1370 | /* 72 */ "BIG", |
| 1371 | /* 73 */ "SMALL", |
| 1372 | /* 74 */ "AND", |
| 1373 | /* 75 */ "LT", |
| 1374 | /* 76 */ "GT", |
| 1375 | /* 77 */ "ON", |
| 1376 | /* 78 */ "WAY", |
| 1377 | /* 79 */ "BETWEEN", |
| 1378 | /* 80 */ "THE", |
| 1379 | /* 81 */ "NTH", |
| 1380 | /* 82 */ "VERTEX", |
| 1381 | /* 83 */ "TOP", |
| 1382 | /* 84 */ "BOTTOM", |
| 1383 | /* 85 */ "START", |
| 1384 | /* 86 */ "END", |
| 1385 | /* 87 */ "IN", |
| 1386 | /* 88 */ "THIS", |
| 1387 | /* 89 */ "DOT_U", |
| 1388 | /* 90 */ "LAST", |
| 1389 | /* 91 */ "NUMBER", |
| 1390 | /* 92 */ "FUNC1", |
| 1391 | /* 93 */ "FUNC2", |
| 1392 | /* 94 */ "DIST", |
| 1393 | /* 95 */ "DOT_XY", |
| 1394 | /* 96 */ "X", |
| 1395 | /* 97 */ "Y", |
| 1396 | /* 98 */ "DOT_L", |
| 1397 | /* 99 */ "statement_list", |
| 1398 | /* 100 */ "statement", |
| 1399 | /* 101 */ "unnamed_statement", |
| 1400 | /* 102 */ "basetype", |
| 1401 | /* 103 */ "expr", |
| 1402 | /* 104 */ "numproperty", |
| 1403 | /* 105 */ "edge", |
| 1404 | /* 106 */ "direction", |
| 1405 | /* 107 */ "dashproperty", |
| 1406 | /* 108 */ "colorproperty", |
| 1407 | /* 109 */ "locproperty", |
| 1408 | /* 110 */ "position", |
| 1409 | /* 111 */ "place", |
| 1410 | /* 112 */ "object", |
| 1411 | /* 113 */ "objectname", |
| 1412 | /* 114 */ "nth", |
| 1413 | /* 115 */ "textposition", |
| 1414 | /* 116 */ "rvalue", |
| 1415 | /* 117 */ "lvalue", |
| 1416 | /* 118 */ "even", |
| 1417 | /* 119 */ "relexpr", |
| 1418 | /* 120 */ "optrelexpr", |
| 1419 | /* 121 */ "document", |
| 1420 | /* 122 */ "print", |
| 1421 | /* 123 */ "prlist", |
| 1422 | /* 124 */ "pritem", |
| 1423 | /* 125 */ "prsep", |
| 1424 | /* 126 */ "attribute_list", |
| 1425 | /* 127 */ "savelist", |
| 1426 | /* 128 */ "alist", |
| 1427 | /* 129 */ "attribute", |
| 1428 | /* 130 */ "go", |
| 1429 | /* 131 */ "boolproperty", |
| 1430 | /* 132 */ "withclause", |
| 1431 | /* 133 */ "between", |
| 1432 | /* 134 */ "place2", |
| 1433 | }; |
| 1434 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1435 | |
| 1436 | #ifndef NDEBUG |
| 1437 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1498,11 +1504,11 @@ | |
| 1498 | /* 58 */ "boolproperty ::= INVIS", |
| 1499 | /* 59 */ "boolproperty ::= THICK", |
| 1500 | /* 60 */ "boolproperty ::= THIN", |
| 1501 | /* 61 */ "boolproperty ::= SOLID", |
| 1502 | /* 62 */ "textposition ::=", |
| 1503 | /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL", |
| 1504 | /* 64 */ "position ::= expr COMMA expr", |
| 1505 | /* 65 */ "position ::= place PLUS expr COMMA expr", |
| 1506 | /* 66 */ "position ::= place MINUS expr COMMA expr", |
| 1507 | /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", |
| 1508 | /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", |
| @@ -1716,24 +1722,24 @@ | |
| 1716 | ** Note: during a reduce, the only symbols destroyed are those |
| 1717 | ** which appear on the RHS of the rule, but which are *not* used |
| 1718 | ** inside the C code. |
| 1719 | */ |
| 1720 | /********* Begin destructor definitions ***************************************/ |
| 1721 | case 99: /* statement_list */ |
| 1722 | { |
| 1723 | #line 509 "pikchr.y" |
| 1724 | pik_elist_free(p,(yypminor->yy227)); |
| 1725 | #line 1750 "pikchr.c" |
| 1726 | } |
| 1727 | break; |
| 1728 | case 100: /* statement */ |
| 1729 | case 101: /* unnamed_statement */ |
| 1730 | case 102: /* basetype */ |
| 1731 | { |
| 1732 | #line 511 "pikchr.y" |
| 1733 | pik_elem_free(p,(yypminor->yy36)); |
| 1734 | #line 1759 "pikchr.c" |
| 1735 | } |
| 1736 | break; |
| 1737 | /********* End destructor definitions *****************************************/ |
| 1738 | default: break; /* If no destructor action specified: do nothing */ |
| 1739 | } |
| @@ -1947,14 +1953,14 @@ | |
| 1947 | #endif |
| 1948 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 1949 | /* Here code is inserted which will execute if the parser |
| 1950 | ** stack every overflows */ |
| 1951 | /******** Begin %stack_overflow code ******************************************/ |
| 1952 | #line 543 "pikchr.y" |
| 1953 | |
| 1954 | pik_error(p, 0, "parser stack overflow"); |
| 1955 | #line 1980 "pikchr.c" |
| 1956 | /******** End %stack_overflow code ********************************************/ |
| 1957 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 1958 | pik_parserCTX_STORE |
| 1959 | } |
| 1960 | |
| @@ -2022,166 +2028,166 @@ | |
| 2022 | } |
| 2023 | |
| 2024 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2025 | ** of that rule */ |
| 2026 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2027 | 121, /* (0) document ::= statement_list */ |
| 2028 | 99, /* (1) statement_list ::= statement */ |
| 2029 | 99, /* (2) statement_list ::= statement_list EOL statement */ |
| 2030 | 100, /* (3) statement ::= */ |
| 2031 | 100, /* (4) statement ::= direction */ |
| 2032 | 100, /* (5) statement ::= lvalue ASSIGN rvalue */ |
| 2033 | 100, /* (6) statement ::= PLACENAME COLON unnamed_statement */ |
| 2034 | 100, /* (7) statement ::= PLACENAME COLON position */ |
| 2035 | 100, /* (8) statement ::= unnamed_statement */ |
| 2036 | 100, /* (9) statement ::= print prlist */ |
| 2037 | 100, /* (10) statement ::= ASSERT LP expr EQ expr RP */ |
| 2038 | 100, /* (11) statement ::= ASSERT LP position EQ position RP */ |
| 2039 | 100, /* (12) statement ::= DEFINE ID CODEBLOCK */ |
| 2040 | 116, /* (13) rvalue ::= PLACENAME */ |
| 2041 | 124, /* (14) pritem ::= FILL */ |
| 2042 | 124, /* (15) pritem ::= COLOR */ |
| 2043 | 124, /* (16) pritem ::= THICKNESS */ |
| 2044 | 124, /* (17) pritem ::= rvalue */ |
| 2045 | 124, /* (18) pritem ::= STRING */ |
| 2046 | 125, /* (19) prsep ::= COMMA */ |
| 2047 | 101, /* (20) unnamed_statement ::= basetype attribute_list */ |
| 2048 | 102, /* (21) basetype ::= CLASSNAME */ |
| 2049 | 102, /* (22) basetype ::= STRING textposition */ |
| 2050 | 102, /* (23) basetype ::= LB savelist statement_list RB */ |
| 2051 | 127, /* (24) savelist ::= */ |
| 2052 | 119, /* (25) relexpr ::= expr */ |
| 2053 | 119, /* (26) relexpr ::= expr PERCENT */ |
| 2054 | 120, /* (27) optrelexpr ::= */ |
| 2055 | 126, /* (28) attribute_list ::= relexpr alist */ |
| 2056 | 129, /* (29) attribute ::= numproperty relexpr */ |
| 2057 | 129, /* (30) attribute ::= dashproperty expr */ |
| 2058 | 129, /* (31) attribute ::= dashproperty */ |
| 2059 | 129, /* (32) attribute ::= colorproperty rvalue */ |
| 2060 | 129, /* (33) attribute ::= go direction optrelexpr */ |
| 2061 | 129, /* (34) attribute ::= go direction even position */ |
| 2062 | 129, /* (35) attribute ::= CLOSE */ |
| 2063 | 129, /* (36) attribute ::= CHOP */ |
| 2064 | 129, /* (37) attribute ::= FROM position */ |
| 2065 | 129, /* (38) attribute ::= TO position */ |
| 2066 | 129, /* (39) attribute ::= THEN */ |
| 2067 | 129, /* (40) attribute ::= THEN optrelexpr HEADING expr */ |
| 2068 | 129, /* (41) attribute ::= THEN optrelexpr EDGEPT */ |
| 2069 | 129, /* (42) attribute ::= GO optrelexpr HEADING expr */ |
| 2070 | 129, /* (43) attribute ::= GO optrelexpr EDGEPT */ |
| 2071 | 129, /* (44) attribute ::= AT position */ |
| 2072 | 129, /* (45) attribute ::= SAME */ |
| 2073 | 129, /* (46) attribute ::= SAME AS object */ |
| 2074 | 129, /* (47) attribute ::= STRING textposition */ |
| 2075 | 129, /* (48) attribute ::= FIT */ |
| 2076 | 129, /* (49) attribute ::= BEHIND object */ |
| 2077 | 132, /* (50) withclause ::= DOT_E edge AT position */ |
| 2078 | 132, /* (51) withclause ::= edge AT position */ |
| 2079 | 104, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2080 | 131, /* (53) boolproperty ::= CW */ |
| 2081 | 131, /* (54) boolproperty ::= CCW */ |
| 2082 | 131, /* (55) boolproperty ::= LARROW */ |
| 2083 | 131, /* (56) boolproperty ::= RARROW */ |
| 2084 | 131, /* (57) boolproperty ::= LRARROW */ |
| 2085 | 131, /* (58) boolproperty ::= INVIS */ |
| 2086 | 131, /* (59) boolproperty ::= THICK */ |
| 2087 | 131, /* (60) boolproperty ::= THIN */ |
| 2088 | 131, /* (61) boolproperty ::= SOLID */ |
| 2089 | 115, /* (62) textposition ::= */ |
| 2090 | 115, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ |
| 2091 | 110, /* (64) position ::= expr COMMA expr */ |
| 2092 | 110, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2093 | 110, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2094 | 110, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2095 | 110, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| 2096 | 110, /* (69) position ::= LP position COMMA position RP */ |
| 2097 | 110, /* (70) position ::= LP position RP */ |
| 2098 | 110, /* (71) position ::= expr between position AND position */ |
| 2099 | 110, /* (72) position ::= expr LT position COMMA position GT */ |
| 2100 | 110, /* (73) position ::= expr ABOVE position */ |
| 2101 | 110, /* (74) position ::= expr BELOW position */ |
| 2102 | 110, /* (75) position ::= expr LEFT OF position */ |
| 2103 | 110, /* (76) position ::= expr RIGHT OF position */ |
| 2104 | 110, /* (77) position ::= expr ON HEADING EDGEPT OF position */ |
| 2105 | 110, /* (78) position ::= expr HEADING EDGEPT OF position */ |
| 2106 | 110, /* (79) position ::= expr EDGEPT OF position */ |
| 2107 | 110, /* (80) position ::= expr ON HEADING expr FROM position */ |
| 2108 | 110, /* (81) position ::= expr HEADING expr FROM position */ |
| 2109 | 111, /* (82) place ::= edge OF object */ |
| 2110 | 134, /* (83) place2 ::= object */ |
| 2111 | 134, /* (84) place2 ::= object DOT_E edge */ |
| 2112 | 134, /* (85) place2 ::= NTH VERTEX OF object */ |
| 2113 | 112, /* (86) object ::= nth */ |
| 2114 | 112, /* (87) object ::= nth OF|IN object */ |
| 2115 | 113, /* (88) objectname ::= THIS */ |
| 2116 | 113, /* (89) objectname ::= PLACENAME */ |
| 2117 | 113, /* (90) objectname ::= objectname DOT_U PLACENAME */ |
| 2118 | 114, /* (91) nth ::= NTH CLASSNAME */ |
| 2119 | 114, /* (92) nth ::= NTH LAST CLASSNAME */ |
| 2120 | 114, /* (93) nth ::= LAST CLASSNAME */ |
| 2121 | 114, /* (94) nth ::= LAST */ |
| 2122 | 114, /* (95) nth ::= NTH LB RB */ |
| 2123 | 114, /* (96) nth ::= NTH LAST LB RB */ |
| 2124 | 114, /* (97) nth ::= LAST LB RB */ |
| 2125 | 103, /* (98) expr ::= expr PLUS expr */ |
| 2126 | 103, /* (99) expr ::= expr MINUS expr */ |
| 2127 | 103, /* (100) expr ::= expr STAR expr */ |
| 2128 | 103, /* (101) expr ::= expr SLASH expr */ |
| 2129 | 103, /* (102) expr ::= MINUS expr */ |
| 2130 | 103, /* (103) expr ::= PLUS expr */ |
| 2131 | 103, /* (104) expr ::= LP expr RP */ |
| 2132 | 103, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2133 | 103, /* (106) expr ::= NUMBER */ |
| 2134 | 103, /* (107) expr ::= ID */ |
| 2135 | 103, /* (108) expr ::= FUNC1 LP expr RP */ |
| 2136 | 103, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2137 | 103, /* (110) expr ::= DIST LP position COMMA position RP */ |
| 2138 | 103, /* (111) expr ::= place2 DOT_XY X */ |
| 2139 | 103, /* (112) expr ::= place2 DOT_XY Y */ |
| 2140 | 103, /* (113) expr ::= object DOT_L numproperty */ |
| 2141 | 103, /* (114) expr ::= object DOT_L dashproperty */ |
| 2142 | 103, /* (115) expr ::= object DOT_L colorproperty */ |
| 2143 | 117, /* (116) lvalue ::= ID */ |
| 2144 | 117, /* (117) lvalue ::= FILL */ |
| 2145 | 117, /* (118) lvalue ::= COLOR */ |
| 2146 | 117, /* (119) lvalue ::= THICKNESS */ |
| 2147 | 116, /* (120) rvalue ::= expr */ |
| 2148 | 122, /* (121) print ::= PRINT */ |
| 2149 | 123, /* (122) prlist ::= pritem */ |
| 2150 | 123, /* (123) prlist ::= prlist prsep pritem */ |
| 2151 | 106, /* (124) direction ::= UP */ |
| 2152 | 106, /* (125) direction ::= DOWN */ |
| 2153 | 106, /* (126) direction ::= LEFT */ |
| 2154 | 106, /* (127) direction ::= RIGHT */ |
| 2155 | 120, /* (128) optrelexpr ::= relexpr */ |
| 2156 | 126, /* (129) attribute_list ::= alist */ |
| 2157 | 128, /* (130) alist ::= */ |
| 2158 | 128, /* (131) alist ::= alist attribute */ |
| 2159 | 129, /* (132) attribute ::= boolproperty */ |
| 2160 | 129, /* (133) attribute ::= WITH withclause */ |
| 2161 | 130, /* (134) go ::= GO */ |
| 2162 | 130, /* (135) go ::= */ |
| 2163 | 118, /* (136) even ::= UNTIL EVEN WITH */ |
| 2164 | 118, /* (137) even ::= EVEN WITH */ |
| 2165 | 107, /* (138) dashproperty ::= DOTTED */ |
| 2166 | 107, /* (139) dashproperty ::= DASHED */ |
| 2167 | 108, /* (140) colorproperty ::= FILL */ |
| 2168 | 108, /* (141) colorproperty ::= COLOR */ |
| 2169 | 110, /* (142) position ::= place */ |
| 2170 | 133, /* (143) between ::= WAY BETWEEN */ |
| 2171 | 133, /* (144) between ::= BETWEEN */ |
| 2172 | 133, /* (145) between ::= OF THE WAY BETWEEN */ |
| 2173 | 111, /* (146) place ::= place2 */ |
| 2174 | 105, /* (147) edge ::= CENTER */ |
| 2175 | 105, /* (148) edge ::= EDGEPT */ |
| 2176 | 105, /* (149) edge ::= TOP */ |
| 2177 | 105, /* (150) edge ::= BOTTOM */ |
| 2178 | 105, /* (151) edge ::= START */ |
| 2179 | 105, /* (152) edge ::= END */ |
| 2180 | 105, /* (153) edge ::= RIGHT */ |
| 2181 | 105, /* (154) edge ::= LEFT */ |
| 2182 | 112, /* (155) object ::= objectname */ |
| 2183 | }; |
| 2184 | |
| 2185 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2186 | ** of symbols on the right-hand side of that rule. */ |
| 2187 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2246,11 +2252,11 @@ | |
| 2246 | -1, /* (58) boolproperty ::= INVIS */ |
| 2247 | -1, /* (59) boolproperty ::= THICK */ |
| 2248 | -1, /* (60) boolproperty ::= THIN */ |
| 2249 | -1, /* (61) boolproperty ::= SOLID */ |
| 2250 | 0, /* (62) textposition ::= */ |
| 2251 | -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ |
| 2252 | -3, /* (64) position ::= expr COMMA expr */ |
| 2253 | -5, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2254 | -5, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2255 | -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2256 | -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| @@ -2381,620 +2387,620 @@ | |
| 2381 | ** break; |
| 2382 | */ |
| 2383 | /********** Begin reduce actions **********************************************/ |
| 2384 | YYMINORTYPE yylhsminor; |
| 2385 | case 0: /* document ::= statement_list */ |
| 2386 | #line 547 "pikchr.y" |
| 2387 | {pik_render(p,yymsp[0].minor.yy227);} |
| 2388 | #line 2413 "pikchr.c" |
| 2389 | break; |
| 2390 | case 1: /* statement_list ::= statement */ |
| 2391 | #line 550 "pikchr.y" |
| 2392 | { yylhsminor.yy227 = pik_elist_append(p,0,yymsp[0].minor.yy36); } |
| 2393 | #line 2418 "pikchr.c" |
| 2394 | yymsp[0].minor.yy227 = yylhsminor.yy227; |
| 2395 | break; |
| 2396 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2397 | #line 552 "pikchr.y" |
| 2398 | { yylhsminor.yy227 = pik_elist_append(p,yymsp[-2].minor.yy227,yymsp[0].minor.yy36); } |
| 2399 | #line 2424 "pikchr.c" |
| 2400 | yymsp[-2].minor.yy227 = yylhsminor.yy227; |
| 2401 | break; |
| 2402 | case 3: /* statement ::= */ |
| 2403 | #line 555 "pikchr.y" |
| 2404 | { yymsp[1].minor.yy36 = 0; } |
| 2405 | #line 2430 "pikchr.c" |
| 2406 | break; |
| 2407 | case 4: /* statement ::= direction */ |
| 2408 | #line 556 "pikchr.y" |
| 2409 | { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy36=0; } |
| 2410 | #line 2435 "pikchr.c" |
| 2411 | yymsp[0].minor.yy36 = yylhsminor.yy36; |
| 2412 | break; |
| 2413 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2414 | #line 557 "pikchr.y" |
| 2415 | {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy153,&yymsp[-1].minor.yy0); yylhsminor.yy36=0;} |
| 2416 | #line 2441 "pikchr.c" |
| 2417 | yymsp[-2].minor.yy36 = yylhsminor.yy36; |
| 2418 | break; |
| 2419 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2420 | #line 559 "pikchr.y" |
| 2421 | { yylhsminor.yy36 = yymsp[0].minor.yy36; pik_elem_setname(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0); } |
| 2422 | #line 2447 "pikchr.c" |
| 2423 | yymsp[-2].minor.yy36 = yylhsminor.yy36; |
| 2424 | break; |
| 2425 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2426 | #line 561 "pikchr.y" |
| 2427 | { yylhsminor.yy36 = pik_elem_new(p,0,0,0); |
| 2428 | if(yylhsminor.yy36){ yylhsminor.yy36->ptAt = yymsp[0].minor.yy79; pik_elem_setname(p,yylhsminor.yy36,&yymsp[-2].minor.yy0); }} |
| 2429 | #line 2454 "pikchr.c" |
| 2430 | yymsp[-2].minor.yy36 = yylhsminor.yy36; |
| 2431 | break; |
| 2432 | case 8: /* statement ::= unnamed_statement */ |
| 2433 | #line 563 "pikchr.y" |
| 2434 | {yylhsminor.yy36 = yymsp[0].minor.yy36;} |
| 2435 | #line 2460 "pikchr.c" |
| 2436 | yymsp[0].minor.yy36 = yylhsminor.yy36; |
| 2437 | break; |
| 2438 | case 9: /* statement ::= print prlist */ |
| 2439 | #line 564 "pikchr.y" |
| 2440 | {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy36=0;} |
| 2441 | #line 2466 "pikchr.c" |
| 2442 | break; |
| 2443 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2444 | #line 569 "pikchr.y" |
| 2445 | {yymsp[-5].minor.yy36=pik_assert(p,yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy153);} |
| 2446 | #line 2471 "pikchr.c" |
| 2447 | break; |
| 2448 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2449 | #line 571 "pikchr.y" |
| 2450 | {yymsp[-5].minor.yy36=pik_position_assert(p,&yymsp[-3].minor.yy79,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy79);} |
| 2451 | #line 2476 "pikchr.c" |
| 2452 | break; |
| 2453 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2454 | #line 572 "pikchr.y" |
| 2455 | {yymsp[-2].minor.yy36=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} |
| 2456 | #line 2481 "pikchr.c" |
| 2457 | break; |
| 2458 | case 13: /* rvalue ::= PLACENAME */ |
| 2459 | #line 583 "pikchr.y" |
| 2460 | {yylhsminor.yy153 = pik_lookup_color(p,&yymsp[0].minor.yy0);} |
| 2461 | #line 2486 "pikchr.c" |
| 2462 | yymsp[0].minor.yy153 = yylhsminor.yy153; |
| 2463 | break; |
| 2464 | case 14: /* pritem ::= FILL */ |
| 2465 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2466 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2467 | #line 588 "pikchr.y" |
| 2468 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2469 | #line 2494 "pikchr.c" |
| 2470 | break; |
| 2471 | case 17: /* pritem ::= rvalue */ |
| 2472 | #line 591 "pikchr.y" |
| 2473 | {pik_append_num(p,"",yymsp[0].minor.yy153);} |
| 2474 | #line 2499 "pikchr.c" |
| 2475 | break; |
| 2476 | case 18: /* pritem ::= STRING */ |
| 2477 | #line 592 "pikchr.y" |
| 2478 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2479 | #line 2504 "pikchr.c" |
| 2480 | break; |
| 2481 | case 19: /* prsep ::= COMMA */ |
| 2482 | #line 593 "pikchr.y" |
| 2483 | {pik_append(p, " ", 1);} |
| 2484 | #line 2509 "pikchr.c" |
| 2485 | break; |
| 2486 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2487 | #line 596 "pikchr.y" |
| 2488 | {yylhsminor.yy36 = yymsp[-1].minor.yy36; pik_after_adding_attributes(p,yylhsminor.yy36);} |
| 2489 | #line 2514 "pikchr.c" |
| 2490 | yymsp[-1].minor.yy36 = yylhsminor.yy36; |
| 2491 | break; |
| 2492 | case 21: /* basetype ::= CLASSNAME */ |
| 2493 | #line 598 "pikchr.y" |
| 2494 | {yylhsminor.yy36 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } |
| 2495 | #line 2520 "pikchr.c" |
| 2496 | yymsp[0].minor.yy36 = yylhsminor.yy36; |
| 2497 | break; |
| 2498 | case 22: /* basetype ::= STRING textposition */ |
| 2499 | #line 600 "pikchr.y" |
| 2500 | {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy164; yylhsminor.yy36 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } |
| 2501 | #line 2526 "pikchr.c" |
| 2502 | yymsp[-1].minor.yy36 = yylhsminor.yy36; |
| 2503 | break; |
| 2504 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2505 | #line 602 "pikchr.y" |
| 2506 | { p->list = yymsp[-2].minor.yy227; yymsp[-3].minor.yy36 = pik_elem_new(p,0,0,yymsp[-1].minor.yy227); if(yymsp[-3].minor.yy36) yymsp[-3].minor.yy36->errTok = yymsp[0].minor.yy0; } |
| 2507 | #line 2532 "pikchr.c" |
| 2508 | break; |
| 2509 | case 24: /* savelist ::= */ |
| 2510 | #line 607 "pikchr.y" |
| 2511 | {yymsp[1].minor.yy227 = p->list; p->list = 0;} |
| 2512 | #line 2537 "pikchr.c" |
| 2513 | break; |
| 2514 | case 25: /* relexpr ::= expr */ |
| 2515 | #line 614 "pikchr.y" |
| 2516 | {yylhsminor.yy10.rAbs = yymsp[0].minor.yy153; yylhsminor.yy10.rRel = 0;} |
| 2517 | #line 2542 "pikchr.c" |
| 2518 | yymsp[0].minor.yy10 = yylhsminor.yy10; |
| 2519 | break; |
| 2520 | case 26: /* relexpr ::= expr PERCENT */ |
| 2521 | #line 615 "pikchr.y" |
| 2522 | {yylhsminor.yy10.rAbs = 0; yylhsminor.yy10.rRel = yymsp[-1].minor.yy153/100;} |
| 2523 | #line 2548 "pikchr.c" |
| 2524 | yymsp[-1].minor.yy10 = yylhsminor.yy10; |
| 2525 | break; |
| 2526 | case 27: /* optrelexpr ::= */ |
| 2527 | #line 617 "pikchr.y" |
| 2528 | {yymsp[1].minor.yy10.rAbs = 0; yymsp[1].minor.yy10.rRel = 1.0;} |
| 2529 | #line 2554 "pikchr.c" |
| 2530 | break; |
| 2531 | case 28: /* attribute_list ::= relexpr alist */ |
| 2532 | #line 619 "pikchr.y" |
| 2533 | {pik_add_direction(p,0,&yymsp[-1].minor.yy10);} |
| 2534 | #line 2559 "pikchr.c" |
| 2535 | break; |
| 2536 | case 29: /* attribute ::= numproperty relexpr */ |
| 2537 | #line 623 "pikchr.y" |
| 2538 | { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10); } |
| 2539 | #line 2564 "pikchr.c" |
| 2540 | break; |
| 2541 | case 30: /* attribute ::= dashproperty expr */ |
| 2542 | #line 624 "pikchr.y" |
| 2543 | { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy153); } |
| 2544 | #line 2569 "pikchr.c" |
| 2545 | break; |
| 2546 | case 31: /* attribute ::= dashproperty */ |
| 2547 | #line 625 "pikchr.y" |
| 2548 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2549 | #line 2574 "pikchr.c" |
| 2550 | break; |
| 2551 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2552 | #line 626 "pikchr.y" |
| 2553 | { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153); } |
| 2554 | #line 2579 "pikchr.c" |
| 2555 | break; |
| 2556 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2557 | #line 627 "pikchr.y" |
| 2558 | { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy10);} |
| 2559 | #line 2584 "pikchr.c" |
| 2560 | break; |
| 2561 | case 34: /* attribute ::= go direction even position */ |
| 2562 | #line 628 "pikchr.y" |
| 2563 | {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79);} |
| 2564 | #line 2589 "pikchr.c" |
| 2565 | break; |
| 2566 | case 35: /* attribute ::= CLOSE */ |
| 2567 | #line 629 "pikchr.y" |
| 2568 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2569 | #line 2594 "pikchr.c" |
| 2570 | break; |
| 2571 | case 36: /* attribute ::= CHOP */ |
| 2572 | #line 630 "pikchr.y" |
| 2573 | { p->cur->bChop = 1; } |
| 2574 | #line 2599 "pikchr.c" |
| 2575 | break; |
| 2576 | case 37: /* attribute ::= FROM position */ |
| 2577 | #line 631 "pikchr.y" |
| 2578 | { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); } |
| 2579 | #line 2604 "pikchr.c" |
| 2580 | break; |
| 2581 | case 38: /* attribute ::= TO position */ |
| 2582 | #line 632 "pikchr.y" |
| 2583 | { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy79); } |
| 2584 | #line 2609 "pikchr.c" |
| 2585 | break; |
| 2586 | case 39: /* attribute ::= THEN */ |
| 2587 | #line 633 "pikchr.y" |
| 2588 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2589 | #line 2614 "pikchr.c" |
| 2590 | break; |
| 2591 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2592 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2593 | #line 635 "pikchr.y" |
| 2594 | {pik_move_hdg(p,&yymsp[-2].minor.yy10,&yymsp[-1].minor.yy0,yymsp[0].minor.yy153,0,&yymsp[-3].minor.yy0);} |
| 2595 | #line 2620 "pikchr.c" |
| 2596 | break; |
| 2597 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2598 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2599 | #line 636 "pikchr.y" |
| 2600 | {pik_move_hdg(p,&yymsp[-1].minor.yy10,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} |
| 2601 | #line 2626 "pikchr.c" |
| 2602 | break; |
| 2603 | case 44: /* attribute ::= AT position */ |
| 2604 | #line 641 "pikchr.y" |
| 2605 | { pik_set_at(p,0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); } |
| 2606 | #line 2631 "pikchr.c" |
| 2607 | break; |
| 2608 | case 45: /* attribute ::= SAME */ |
| 2609 | #line 643 "pikchr.y" |
| 2610 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2611 | #line 2636 "pikchr.c" |
| 2612 | break; |
| 2613 | case 46: /* attribute ::= SAME AS object */ |
| 2614 | #line 644 "pikchr.y" |
| 2615 | {pik_same(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} |
| 2616 | #line 2641 "pikchr.c" |
| 2617 | break; |
| 2618 | case 47: /* attribute ::= STRING textposition */ |
| 2619 | #line 645 "pikchr.y" |
| 2620 | {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy164);} |
| 2621 | #line 2646 "pikchr.c" |
| 2622 | break; |
| 2623 | case 48: /* attribute ::= FIT */ |
| 2624 | #line 646 "pikchr.y" |
| 2625 | {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } |
| 2626 | #line 2651 "pikchr.c" |
| 2627 | break; |
| 2628 | case 49: /* attribute ::= BEHIND object */ |
| 2629 | #line 647 "pikchr.y" |
| 2630 | {pik_behind(p,yymsp[0].minor.yy36);} |
| 2631 | #line 2656 "pikchr.c" |
| 2632 | break; |
| 2633 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2634 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2635 | #line 655 "pikchr.y" |
| 2636 | { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy79,&yymsp[-1].minor.yy0); } |
| 2637 | #line 2662 "pikchr.c" |
| 2638 | break; |
| 2639 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2640 | #line 659 "pikchr.y" |
| 2641 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2642 | #line 2667 "pikchr.c" |
| 2643 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2644 | break; |
| 2645 | case 53: /* boolproperty ::= CW */ |
| 2646 | #line 670 "pikchr.y" |
| 2647 | {p->cur->cw = 1;} |
| 2648 | #line 2673 "pikchr.c" |
| 2649 | break; |
| 2650 | case 54: /* boolproperty ::= CCW */ |
| 2651 | #line 671 "pikchr.y" |
| 2652 | {p->cur->cw = 0;} |
| 2653 | #line 2678 "pikchr.c" |
| 2654 | break; |
| 2655 | case 55: /* boolproperty ::= LARROW */ |
| 2656 | #line 672 "pikchr.y" |
| 2657 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2658 | #line 2683 "pikchr.c" |
| 2659 | break; |
| 2660 | case 56: /* boolproperty ::= RARROW */ |
| 2661 | #line 673 "pikchr.y" |
| 2662 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2663 | #line 2688 "pikchr.c" |
| 2664 | break; |
| 2665 | case 57: /* boolproperty ::= LRARROW */ |
| 2666 | #line 674 "pikchr.y" |
| 2667 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2668 | #line 2693 "pikchr.c" |
| 2669 | break; |
| 2670 | case 58: /* boolproperty ::= INVIS */ |
| 2671 | #line 675 "pikchr.y" |
| 2672 | {p->cur->sw = 0.0;} |
| 2673 | #line 2698 "pikchr.c" |
| 2674 | break; |
| 2675 | case 59: /* boolproperty ::= THICK */ |
| 2676 | #line 676 "pikchr.y" |
| 2677 | {p->cur->sw *= 1.5;} |
| 2678 | #line 2703 "pikchr.c" |
| 2679 | break; |
| 2680 | case 60: /* boolproperty ::= THIN */ |
| 2681 | #line 677 "pikchr.y" |
| 2682 | {p->cur->sw *= 0.67;} |
| 2683 | #line 2708 "pikchr.c" |
| 2684 | break; |
| 2685 | case 61: /* boolproperty ::= SOLID */ |
| 2686 | #line 678 "pikchr.y" |
| 2687 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2688 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2689 | #line 2714 "pikchr.c" |
| 2690 | break; |
| 2691 | case 62: /* textposition ::= */ |
| 2692 | #line 681 "pikchr.y" |
| 2693 | {yymsp[1].minor.yy164 = 0;} |
| 2694 | #line 2719 "pikchr.c" |
| 2695 | break; |
| 2696 | case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|ALIGNED|BIG|SMALL */ |
| 2697 | #line 684 "pikchr.y" |
| 2698 | {yylhsminor.yy164 = (short int)pik_text_position(yymsp[-1].minor.yy164,&yymsp[0].minor.yy0);} |
| 2699 | #line 2724 "pikchr.c" |
| 2700 | yymsp[-1].minor.yy164 = yylhsminor.yy164; |
| 2701 | break; |
| 2702 | case 64: /* position ::= expr COMMA expr */ |
| 2703 | #line 687 "pikchr.y" |
| 2704 | {yylhsminor.yy79.x=yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[0].minor.yy153;} |
| 2705 | #line 2730 "pikchr.c" |
| 2706 | yymsp[-2].minor.yy79 = yylhsminor.yy79; |
| 2707 | break; |
| 2708 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2709 | #line 689 "pikchr.y" |
| 2710 | {yylhsminor.yy79.x=yymsp[-4].minor.yy79.x+yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y+yymsp[0].minor.yy153;} |
| 2711 | #line 2736 "pikchr.c" |
| 2712 | yymsp[-4].minor.yy79 = yylhsminor.yy79; |
| 2713 | break; |
| 2714 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2715 | #line 690 "pikchr.y" |
| 2716 | {yylhsminor.yy79.x=yymsp[-4].minor.yy79.x-yymsp[-2].minor.yy153; yylhsminor.yy79.y=yymsp[-4].minor.yy79.y-yymsp[0].minor.yy153;} |
| 2717 | #line 2742 "pikchr.c" |
| 2718 | yymsp[-4].minor.yy79 = yylhsminor.yy79; |
| 2719 | break; |
| 2720 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2721 | #line 692 "pikchr.y" |
| 2722 | {yylhsminor.yy79.x=yymsp[-6].minor.yy79.x+yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y+yymsp[-1].minor.yy153;} |
| 2723 | #line 2748 "pikchr.c" |
| 2724 | yymsp[-6].minor.yy79 = yylhsminor.yy79; |
| 2725 | break; |
| 2726 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2727 | #line 694 "pikchr.y" |
| 2728 | {yylhsminor.yy79.x=yymsp[-6].minor.yy79.x-yymsp[-3].minor.yy153; yylhsminor.yy79.y=yymsp[-6].minor.yy79.y-yymsp[-1].minor.yy153;} |
| 2729 | #line 2754 "pikchr.c" |
| 2730 | yymsp[-6].minor.yy79 = yylhsminor.yy79; |
| 2731 | break; |
| 2732 | case 69: /* position ::= LP position COMMA position RP */ |
| 2733 | #line 695 "pikchr.y" |
| 2734 | {yymsp[-4].minor.yy79.x=yymsp[-3].minor.yy79.x; yymsp[-4].minor.yy79.y=yymsp[-1].minor.yy79.y;} |
| 2735 | #line 2760 "pikchr.c" |
| 2736 | break; |
| 2737 | case 70: /* position ::= LP position RP */ |
| 2738 | #line 696 "pikchr.y" |
| 2739 | {yymsp[-2].minor.yy79=yymsp[-1].minor.yy79;} |
| 2740 | #line 2765 "pikchr.c" |
| 2741 | break; |
| 2742 | case 71: /* position ::= expr between position AND position */ |
| 2743 | #line 698 "pikchr.y" |
| 2744 | {yylhsminor.yy79 = pik_position_between(yymsp[-4].minor.yy153,yymsp[-2].minor.yy79,yymsp[0].minor.yy79);} |
| 2745 | #line 2770 "pikchr.c" |
| 2746 | yymsp[-4].minor.yy79 = yylhsminor.yy79; |
| 2747 | break; |
| 2748 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2749 | #line 700 "pikchr.y" |
| 2750 | {yylhsminor.yy79 = pik_position_between(yymsp[-5].minor.yy153,yymsp[-3].minor.yy79,yymsp[-1].minor.yy79);} |
| 2751 | #line 2776 "pikchr.c" |
| 2752 | yymsp[-5].minor.yy79 = yylhsminor.yy79; |
| 2753 | break; |
| 2754 | case 73: /* position ::= expr ABOVE position */ |
| 2755 | #line 701 "pikchr.y" |
| 2756 | {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y += yymsp[-2].minor.yy153;} |
| 2757 | #line 2782 "pikchr.c" |
| 2758 | yymsp[-2].minor.yy79 = yylhsminor.yy79; |
| 2759 | break; |
| 2760 | case 74: /* position ::= expr BELOW position */ |
| 2761 | #line 702 "pikchr.y" |
| 2762 | {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.y -= yymsp[-2].minor.yy153;} |
| 2763 | #line 2788 "pikchr.c" |
| 2764 | yymsp[-2].minor.yy79 = yylhsminor.yy79; |
| 2765 | break; |
| 2766 | case 75: /* position ::= expr LEFT OF position */ |
| 2767 | #line 703 "pikchr.y" |
| 2768 | {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x -= yymsp[-3].minor.yy153;} |
| 2769 | #line 2794 "pikchr.c" |
| 2770 | yymsp[-3].minor.yy79 = yylhsminor.yy79; |
| 2771 | break; |
| 2772 | case 76: /* position ::= expr RIGHT OF position */ |
| 2773 | #line 704 "pikchr.y" |
| 2774 | {yylhsminor.yy79=yymsp[0].minor.yy79; yylhsminor.yy79.x += yymsp[-3].minor.yy153;} |
| 2775 | #line 2800 "pikchr.c" |
| 2776 | yymsp[-3].minor.yy79 = yylhsminor.yy79; |
| 2777 | break; |
| 2778 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2779 | #line 706 "pikchr.y" |
| 2780 | {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-5].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} |
| 2781 | #line 2806 "pikchr.c" |
| 2782 | yymsp[-5].minor.yy79 = yylhsminor.yy79; |
| 2783 | break; |
| 2784 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2785 | #line 708 "pikchr.y" |
| 2786 | {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-4].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} |
| 2787 | #line 2812 "pikchr.c" |
| 2788 | yymsp[-4].minor.yy79 = yylhsminor.yy79; |
| 2789 | break; |
| 2790 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2791 | #line 710 "pikchr.y" |
| 2792 | {yylhsminor.yy79 = pik_position_at_hdg(yymsp[-3].minor.yy153,&yymsp[-2].minor.yy0,yymsp[0].minor.yy79);} |
| 2793 | #line 2818 "pikchr.c" |
| 2794 | yymsp[-3].minor.yy79 = yylhsminor.yy79; |
| 2795 | break; |
| 2796 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2797 | #line 712 "pikchr.y" |
| 2798 | {yylhsminor.yy79 = pik_position_at_angle(yymsp[-5].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);} |
| 2799 | #line 2824 "pikchr.c" |
| 2800 | yymsp[-5].minor.yy79 = yylhsminor.yy79; |
| 2801 | break; |
| 2802 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2803 | #line 714 "pikchr.y" |
| 2804 | {yylhsminor.yy79 = pik_position_at_angle(yymsp[-4].minor.yy153,yymsp[-2].minor.yy153,yymsp[0].minor.yy79);} |
| 2805 | #line 2830 "pikchr.c" |
| 2806 | yymsp[-4].minor.yy79 = yylhsminor.yy79; |
| 2807 | break; |
| 2808 | case 82: /* place ::= edge OF object */ |
| 2809 | #line 726 "pikchr.y" |
| 2810 | {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} |
| 2811 | #line 2836 "pikchr.c" |
| 2812 | yymsp[-2].minor.yy79 = yylhsminor.yy79; |
| 2813 | break; |
| 2814 | case 83: /* place2 ::= object */ |
| 2815 | #line 727 "pikchr.y" |
| 2816 | {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[0].minor.yy36,0);} |
| 2817 | #line 2842 "pikchr.c" |
| 2818 | yymsp[0].minor.yy79 = yylhsminor.yy79; |
| 2819 | break; |
| 2820 | case 84: /* place2 ::= object DOT_E edge */ |
| 2821 | #line 728 "pikchr.y" |
| 2822 | {yylhsminor.yy79 = pik_place_of_elem(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} |
| 2823 | #line 2848 "pikchr.c" |
| 2824 | yymsp[-2].minor.yy79 = yylhsminor.yy79; |
| 2825 | break; |
| 2826 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2827 | #line 729 "pikchr.y" |
| 2828 | {yylhsminor.yy79 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy36);} |
| 2829 | #line 2854 "pikchr.c" |
| 2830 | yymsp[-3].minor.yy79 = yylhsminor.yy79; |
| 2831 | break; |
| 2832 | case 86: /* object ::= nth */ |
| 2833 | #line 741 "pikchr.y" |
| 2834 | {yylhsminor.yy36 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} |
| 2835 | #line 2860 "pikchr.c" |
| 2836 | yymsp[0].minor.yy36 = yylhsminor.yy36; |
| 2837 | break; |
| 2838 | case 87: /* object ::= nth OF|IN object */ |
| 2839 | #line 742 "pikchr.y" |
| 2840 | {yylhsminor.yy36 = pik_find_nth(p,yymsp[0].minor.yy36,&yymsp[-2].minor.yy0);} |
| 2841 | #line 2866 "pikchr.c" |
| 2842 | yymsp[-2].minor.yy36 = yylhsminor.yy36; |
| 2843 | break; |
| 2844 | case 88: /* objectname ::= THIS */ |
| 2845 | #line 744 "pikchr.y" |
| 2846 | {yymsp[0].minor.yy36 = p->cur;} |
| 2847 | #line 2872 "pikchr.c" |
| 2848 | break; |
| 2849 | case 89: /* objectname ::= PLACENAME */ |
| 2850 | #line 745 "pikchr.y" |
| 2851 | {yylhsminor.yy36 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} |
| 2852 | #line 2877 "pikchr.c" |
| 2853 | yymsp[0].minor.yy36 = yylhsminor.yy36; |
| 2854 | break; |
| 2855 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2856 | #line 747 "pikchr.y" |
| 2857 | {yylhsminor.yy36 = pik_find_byname(p,yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} |
| 2858 | #line 2883 "pikchr.c" |
| 2859 | yymsp[-2].minor.yy36 = yylhsminor.yy36; |
| 2860 | break; |
| 2861 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2862 | #line 749 "pikchr.y" |
| 2863 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2864 | #line 2889 "pikchr.c" |
| 2865 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2866 | break; |
| 2867 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2868 | #line 750 "pikchr.y" |
| 2869 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2870 | #line 2895 "pikchr.c" |
| 2871 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2872 | break; |
| 2873 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2874 | #line 751 "pikchr.y" |
| 2875 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2876 | #line 2901 "pikchr.c" |
| 2877 | break; |
| 2878 | case 94: /* nth ::= LAST */ |
| 2879 | #line 752 "pikchr.y" |
| 2880 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2881 | #line 2906 "pikchr.c" |
| 2882 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2883 | break; |
| 2884 | case 95: /* nth ::= NTH LB RB */ |
| 2885 | #line 753 "pikchr.y" |
| 2886 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2887 | #line 2912 "pikchr.c" |
| 2888 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2889 | break; |
| 2890 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2891 | #line 754 "pikchr.y" |
| 2892 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2893 | #line 2918 "pikchr.c" |
| 2894 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2895 | break; |
| 2896 | case 97: /* nth ::= LAST LB RB */ |
| 2897 | #line 755 "pikchr.y" |
| 2898 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2899 | #line 2924 "pikchr.c" |
| 2900 | break; |
| 2901 | case 98: /* expr ::= expr PLUS expr */ |
| 2902 | #line 757 "pikchr.y" |
| 2903 | {yylhsminor.yy153=yymsp[-2].minor.yy153+yymsp[0].minor.yy153;} |
| 2904 | #line 2929 "pikchr.c" |
| 2905 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2906 | break; |
| 2907 | case 99: /* expr ::= expr MINUS expr */ |
| 2908 | #line 758 "pikchr.y" |
| 2909 | {yylhsminor.yy153=yymsp[-2].minor.yy153-yymsp[0].minor.yy153;} |
| 2910 | #line 2935 "pikchr.c" |
| 2911 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2912 | break; |
| 2913 | case 100: /* expr ::= expr STAR expr */ |
| 2914 | #line 759 "pikchr.y" |
| 2915 | {yylhsminor.yy153=yymsp[-2].minor.yy153*yymsp[0].minor.yy153;} |
| 2916 | #line 2941 "pikchr.c" |
| 2917 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2918 | break; |
| 2919 | case 101: /* expr ::= expr SLASH expr */ |
| 2920 | #line 760 "pikchr.y" |
| 2921 | { |
| 2922 | if( yymsp[0].minor.yy153==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy153 = 0.0; } |
| 2923 | else{ yylhsminor.yy153 = yymsp[-2].minor.yy153/yymsp[0].minor.yy153; } |
| 2924 | } |
| 2925 | #line 2950 "pikchr.c" |
| 2926 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2927 | break; |
| 2928 | case 102: /* expr ::= MINUS expr */ |
| 2929 | #line 764 "pikchr.y" |
| 2930 | {yymsp[-1].minor.yy153=-yymsp[0].minor.yy153;} |
| 2931 | #line 2956 "pikchr.c" |
| 2932 | break; |
| 2933 | case 103: /* expr ::= PLUS expr */ |
| 2934 | #line 765 "pikchr.y" |
| 2935 | {yymsp[-1].minor.yy153=yymsp[0].minor.yy153;} |
| 2936 | #line 2961 "pikchr.c" |
| 2937 | break; |
| 2938 | case 104: /* expr ::= LP expr RP */ |
| 2939 | #line 766 "pikchr.y" |
| 2940 | {yymsp[-2].minor.yy153=yymsp[-1].minor.yy153;} |
| 2941 | #line 2966 "pikchr.c" |
| 2942 | break; |
| 2943 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2944 | #line 767 "pikchr.y" |
| 2945 | {yymsp[-2].minor.yy153=pik_get_var(p,&yymsp[-1].minor.yy0);} |
| 2946 | #line 2971 "pikchr.c" |
| 2947 | break; |
| 2948 | case 106: /* expr ::= NUMBER */ |
| 2949 | #line 768 "pikchr.y" |
| 2950 | {yylhsminor.yy153=pik_atof(&yymsp[0].minor.yy0);} |
| 2951 | #line 2976 "pikchr.c" |
| 2952 | yymsp[0].minor.yy153 = yylhsminor.yy153; |
| 2953 | break; |
| 2954 | case 107: /* expr ::= ID */ |
| 2955 | #line 769 "pikchr.y" |
| 2956 | {yylhsminor.yy153=pik_get_var(p,&yymsp[0].minor.yy0);} |
| 2957 | #line 2982 "pikchr.c" |
| 2958 | yymsp[0].minor.yy153 = yylhsminor.yy153; |
| 2959 | break; |
| 2960 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 2961 | #line 770 "pikchr.y" |
| 2962 | {yylhsminor.yy153 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy153,0.0);} |
| 2963 | #line 2988 "pikchr.c" |
| 2964 | yymsp[-3].minor.yy153 = yylhsminor.yy153; |
| 2965 | break; |
| 2966 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2967 | #line 771 "pikchr.y" |
| 2968 | {yylhsminor.yy153 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy153,yymsp[-1].minor.yy153);} |
| 2969 | #line 2994 "pikchr.c" |
| 2970 | yymsp[-5].minor.yy153 = yylhsminor.yy153; |
| 2971 | break; |
| 2972 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 2973 | #line 772 "pikchr.y" |
| 2974 | {yymsp[-5].minor.yy153 = pik_dist(&yymsp[-3].minor.yy79,&yymsp[-1].minor.yy79);} |
| 2975 | #line 3000 "pikchr.c" |
| 2976 | break; |
| 2977 | case 111: /* expr ::= place2 DOT_XY X */ |
| 2978 | #line 773 "pikchr.y" |
| 2979 | {yylhsminor.yy153 = yymsp[-2].minor.yy79.x;} |
| 2980 | #line 3005 "pikchr.c" |
| 2981 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2982 | break; |
| 2983 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 2984 | #line 774 "pikchr.y" |
| 2985 | {yylhsminor.yy153 = yymsp[-2].minor.yy79.y;} |
| 2986 | #line 3011 "pikchr.c" |
| 2987 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2988 | break; |
| 2989 | case 113: /* expr ::= object DOT_L numproperty */ |
| 2990 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 2991 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 2992 | #line 775 "pikchr.y" |
| 2993 | {yylhsminor.yy153=pik_property_of(yymsp[-2].minor.yy36,&yymsp[0].minor.yy0);} |
| 2994 | #line 3019 "pikchr.c" |
| 2995 | yymsp[-2].minor.yy153 = yylhsminor.yy153; |
| 2996 | break; |
| 2997 | default: |
| 2998 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 2999 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3000 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3092,19 +3098,19 @@ | |
| 3092 | ){ |
| 3093 | pik_parserARG_FETCH |
| 3094 | pik_parserCTX_FETCH |
| 3095 | #define TOKEN yyminor |
| 3096 | /************ Begin %syntax_error code ****************************************/ |
| 3097 | #line 535 "pikchr.y" |
| 3098 | |
| 3099 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3100 | pik_error(p, &TOKEN, "syntax error"); |
| 3101 | }else{ |
| 3102 | pik_error(p, 0, "syntax error"); |
| 3103 | } |
| 3104 | UNUSED_PARAMETER(yymajor); |
| 3105 | #line 3130 "pikchr.c" |
| 3106 | /************ End %syntax_error code ******************************************/ |
| 3107 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3108 | pik_parserCTX_STORE |
| 3109 | } |
| 3110 | |
| @@ -3376,11 +3382,11 @@ | |
| 3376 | #else |
| 3377 | (void)iToken; |
| 3378 | return 0; |
| 3379 | #endif |
| 3380 | } |
| 3381 | #line 780 "pikchr.y" |
| 3382 | |
| 3383 | |
| 3384 | |
| 3385 | /* Chart of the 148 official CSS color names with their |
| 3386 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -4978,14 +4984,16 @@ | |
| 4978 | if( t->eCode & TP_RJUST ) nx += jw; |
| 4979 | |
| 4980 | if( pBox!=0 ){ |
| 4981 | /* If pBox is not NULL, do not draw any <text>. Instead, just expand |
| 4982 | ** pBox to include the text */ |
| 4983 | PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale*0.01; |
| 4984 | PNum ch = p->charHeight*0.5*xtraFontScale; |
| 4985 | PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ |
| 4986 | if( t->eCode & TP_BOLD ) cw *= 1.1; |
| 4987 | if( t->eCode & TP_RJUST ){ |
| 4988 | x0 = nx; |
| 4989 | y0 = y-ch; |
| 4990 | x1 = nx-cw; |
| 4991 | y1 = y+ch; |
| @@ -5037,10 +5045,13 @@ | |
| 5037 | pik_append(p, " font-style=\"italic\"", -1); |
| 5038 | } |
| 5039 | if( t->eCode & TP_BOLD ){ |
| 5040 | pik_append(p, " font-weight=\"bold\"", -1); |
| 5041 | } |
| 5042 | if( pObj->color>=0.0 ){ |
| 5043 | pik_append_clr(p, " fill=\"", pObj->color, "\"",0); |
| 5044 | } |
| 5045 | xtraFontScale *= p->fontScale; |
| 5046 | if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ |
| @@ -6044,12 +6055,13 @@ | |
| 6044 | case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; |
| 6045 | case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; |
| 6046 | case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; |
| 6047 | case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; |
| 6048 | case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; |
| 6049 | case T_ITALIC: iRes |= TP_ITALIC; break; |
| 6050 | case T_BOLD: iRes |= TP_BOLD; break; |
| 6051 | case T_ALIGNED: iRes |= TP_ALIGN; break; |
| 6052 | case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; |
| 6053 | else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; |
| 6054 | case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; |
| 6055 | else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; |
| @@ -6180,15 +6192,17 @@ | |
| 6180 | ** |
| 6181 | ** Omit "\" used to escape characters. And count entities like |
| 6182 | ** "<" as a single character. Multi-byte UTF8 characters count |
| 6183 | ** as a single character. |
| 6184 | ** |
| 6185 | ** Attempt to scale the answer by the actual characters seen. Wide |
| 6186 | ** characters count more than narrow characters. But the widths are |
| 6187 | ** only guesses. |
| 6188 | */ |
| 6189 | static int pik_text_length(const PToken *pToken){ |
| 6190 | int n = pToken->n; |
| 6191 | const char *z = pToken->z; |
| 6192 | int cnt, j; |
| 6193 | for(j=1, cnt=0; j<n-1; j++){ |
| 6194 | char c = z[j]; |
| @@ -6196,22 +6210,24 @@ | |
| 6196 | c = z[++j]; |
| 6197 | }else if( c=='&' ){ |
| 6198 | int k; |
| 6199 | for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} |
| 6200 | if( z[k]==';' ) j = k; |
| 6201 | cnt += 150; |
| 6202 | continue; |
| 6203 | } |
| 6204 | if( (c & 0xc0)==0xc0 ){ |
| 6205 | while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; } |
| 6206 | cnt += 100; |
| 6207 | continue; |
| 6208 | } |
| 6209 | if( c>=0x20 && c<=0x7e ){ |
| 6210 | cnt += awChar[c-0x20]; |
| 6211 | }else{ |
| 6212 | cnt += 100; |
| 6213 | } |
| 6214 | } |
| 6215 | return cnt; |
| 6216 | } |
| 6217 | |
| @@ -7195,10 +7211,12 @@ | |
| 7195 | { "last", 4, T_LAST, 0, 0 }, |
| 7196 | { "left", 4, T_LEFT, DIR_LEFT, CP_W }, |
| 7197 | { "ljust", 5, T_LJUST, 0, 0 }, |
| 7198 | { "max", 3, T_FUNC2, FN_MAX, 0 }, |
| 7199 | { "min", 3, T_FUNC2, FN_MIN, 0 }, |
| 7200 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7201 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7202 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7203 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7204 | { "of", 2, T_OF, 0, 0 }, |
| @@ -8122,6 +8140,6 @@ | |
| 8122 | |
| 8123 | |
| 8124 | #endif /* PIKCHR_TCL */ |
| 8125 | |
| 8126 | |
| 8127 | #line 8152 "pikchr.c" |
| 8128 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -59,11 +59,11 @@ | |
| 59 | ** cc pikchr.c -o pikchr.o |
| 60 | ** |
| 61 | ** Add -DPIKCHR_SHELL to add a main() routine that reads input files |
| 62 | ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for |
| 63 | ** -fsanitizer=fuzzer testing. |
| 64 | ** |
| 65 | **************************************************************************** |
| 66 | ** IMPLEMENTATION NOTES (for people who want to understand the internal |
| 67 | ** operation of this software, perhaps to extend the code or to fix bugs): |
| 68 | ** |
| 69 | ** Each call to pikchr() uses a single instance of the Pik structure to |
| @@ -203,12 +203,13 @@ | |
| 203 | #define TP_SMALL 0x0200 /* Smaller font */ |
| 204 | #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ |
| 205 | #define TP_SZMASK 0x0700 /* Font size mask */ |
| 206 | #define TP_ITALIC 0x1000 /* Italic font */ |
| 207 | #define TP_BOLD 0x2000 /* Bold font */ |
| 208 | #define TP_MONO 0x4000 /* Monospace font family */ |
| 209 | #define TP_FMASK 0x7000 /* Mask for font style */ |
| 210 | #define TP_ALIGN 0x8000 /* Rotate to align with the line */ |
| 211 | |
| 212 | /* An object to hold a position in 2-D space */ |
| 213 | struct PPoint { |
| 214 | PNum x, y; /* X and Y coordinates */ |
| 215 | }; |
| @@ -470,11 +471,11 @@ | |
| 471 | static void pik_bbox_init(PBox*); |
| 472 | static void pik_bbox_addbox(PBox*,PBox*); |
| 473 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 474 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 475 | static void pik_add_txt(Pik*,PToken*,int); |
| 476 | static int pik_text_length(const PToken *pToken, const int isMonospace); |
| 477 | static void pik_size_to_fit(Pik*,PToken*,int); |
| 478 | static int pik_text_position(int,PToken*); |
| 479 | static PNum pik_property_of(PObj*,PToken*); |
| 480 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 481 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| @@ -489,11 +490,11 @@ | |
| 490 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 491 | static PNum pik_dist(PPoint*,PPoint*); |
| 492 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 493 | |
| 494 | |
| 495 | #line 521 "pikchr.c" |
| 496 | /**************** End of %include directives **********************************/ |
| 497 | /* These constants specify the various numeric values for terminal symbols. |
| 498 | ***************** Begin token definitions *************************************/ |
| 499 | #ifndef T_ID |
| 500 | #define T_ID 1 |
| @@ -564,38 +565,39 @@ | |
| 565 | #define T_RJUST 66 |
| 566 | #define T_ABOVE 67 |
| 567 | #define T_BELOW 68 |
| 568 | #define T_ITALIC 69 |
| 569 | #define T_BOLD 70 |
| 570 | #define T_MONO 71 |
| 571 | #define T_ALIGNED 72 |
| 572 | #define T_BIG 73 |
| 573 | #define T_SMALL 74 |
| 574 | #define T_AND 75 |
| 575 | #define T_LT 76 |
| 576 | #define T_GT 77 |
| 577 | #define T_ON 78 |
| 578 | #define T_WAY 79 |
| 579 | #define T_BETWEEN 80 |
| 580 | #define T_THE 81 |
| 581 | #define T_NTH 82 |
| 582 | #define T_VERTEX 83 |
| 583 | #define T_TOP 84 |
| 584 | #define T_BOTTOM 85 |
| 585 | #define T_START 86 |
| 586 | #define T_END 87 |
| 587 | #define T_IN 88 |
| 588 | #define T_THIS 89 |
| 589 | #define T_DOT_U 90 |
| 590 | #define T_LAST 91 |
| 591 | #define T_NUMBER 92 |
| 592 | #define T_FUNC1 93 |
| 593 | #define T_FUNC2 94 |
| 594 | #define T_DIST 95 |
| 595 | #define T_DOT_XY 96 |
| 596 | #define T_X 97 |
| 597 | #define T_Y 98 |
| 598 | #define T_DOT_L 99 |
| 599 | #endif |
| 600 | /**************** End token definitions ***************************************/ |
| 601 | |
| 602 | /* The next sections is a series of control #defines. |
| 603 | ** various aspects of the generated parser. |
| @@ -651,22 +653,22 @@ | |
| 653 | #ifndef INTERFACE |
| 654 | # define INTERFACE 1 |
| 655 | #endif |
| 656 | /************* Begin control #defines *****************************************/ |
| 657 | #define YYCODETYPE unsigned char |
| 658 | #define YYNOCODE 136 |
| 659 | #define YYACTIONTYPE unsigned short int |
| 660 | #define pik_parserTOKENTYPE PToken |
| 661 | typedef union { |
| 662 | int yyinit; |
| 663 | pik_parserTOKENTYPE yy0; |
| 664 | PNum yy21; |
| 665 | PPoint yy63; |
| 666 | PRel yy72; |
| 667 | PObj* yy162; |
| 668 | short int yy188; |
| 669 | PList* yy235; |
| 670 | } YYMINORTYPE; |
| 671 | #ifndef YYSTACKDEPTH |
| 672 | #define YYSTACKDEPTH 100 |
| 673 | #endif |
| 674 | #define pik_parserARG_SDECL |
| @@ -681,11 +683,11 @@ | |
| 683 | #define pik_parserCTX_STORE yypParser->p=p; |
| 684 | #define YYFALLBACK 1 |
| 685 | #define YYNSTATE 164 |
| 686 | #define YYNRULE 156 |
| 687 | #define YYNRULE_WITH_ACTION 116 |
| 688 | #define YYNTOKEN 100 |
| 689 | #define YY_MAX_SHIFT 163 |
| 690 | #define YY_MIN_SHIFTREDUCE 287 |
| 691 | #define YY_MAX_SHIFTREDUCE 442 |
| 692 | #define YY_ERROR_ACTION 443 |
| 693 | #define YY_ACCEPT_ACTION 444 |
| @@ -756,322 +758,324 @@ | |
| 758 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 759 | ** shifting non-terminals after a reduce. |
| 760 | ** yy_default[] Default action for each state. |
| 761 | ** |
| 762 | *********** Begin parsing tables **********************************************/ |
| 763 | #define YY_ACTTAB_COUNT (1313) |
| 764 | static const YYACTIONTYPE yy_action[] = { |
| 765 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 766 | /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, |
| 767 | /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, |
| 768 | /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, |
| 769 | /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, |
| 770 | /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, |
| 771 | /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, |
| 772 | /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, |
| 773 | /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, |
| 774 | /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, |
| 775 | /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, |
| 776 | /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, |
| 777 | /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, |
| 778 | /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, |
| 779 | /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, |
| 780 | /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, |
| 781 | /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, |
| 782 | /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, |
| 783 | /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, |
| 784 | /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, |
| 785 | /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, |
| 786 | /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, |
| 787 | /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, |
| 788 | /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, |
| 789 | /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, |
| 790 | /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, |
| 791 | /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, |
| 792 | /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, |
| 793 | /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, |
| 794 | /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, |
| 795 | /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, |
| 796 | /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, |
| 797 | /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, |
| 798 | /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, |
| 799 | /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, |
| 800 | /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, |
| 801 | /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, |
| 802 | /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, |
| 803 | /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, |
| 804 | /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, |
| 805 | /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, |
| 806 | /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, |
| 807 | /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, |
| 808 | /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, |
| 809 | /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, |
| 810 | /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, |
| 811 | /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, |
| 812 | /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, |
| 813 | /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, |
| 814 | /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, |
| 815 | /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, |
| 816 | /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, |
| 817 | /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, |
| 818 | /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, |
| 819 | /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, |
| 820 | /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, |
| 821 | /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, |
| 822 | /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, |
| 823 | /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, |
| 824 | /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, |
| 825 | /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, |
| 826 | /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, |
| 827 | /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, |
| 828 | /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, |
| 829 | /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, |
| 830 | /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, |
| 831 | /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, |
| 832 | /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, |
| 833 | /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, |
| 834 | /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, |
| 835 | /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, |
| 836 | /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, |
| 837 | /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, |
| 838 | /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, |
| 839 | /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, |
| 840 | /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, |
| 841 | /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, |
| 842 | /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, |
| 843 | /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, |
| 844 | /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, |
| 845 | /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, |
| 846 | /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, |
| 847 | /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, |
| 848 | /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, |
| 849 | /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, |
| 850 | /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, |
| 851 | /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, |
| 852 | /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, |
| 853 | /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, |
| 854 | /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, |
| 855 | /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, |
| 856 | /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, |
| 857 | /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, |
| 858 | /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, |
| 859 | /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, |
| 860 | /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, |
| 861 | /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, |
| 862 | /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, |
| 863 | /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, |
| 864 | /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, |
| 865 | /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, |
| 866 | /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, |
| 867 | /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, |
| 868 | /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, |
| 869 | /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, |
| 870 | /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, |
| 871 | /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, |
| 872 | /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, |
| 873 | /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, |
| 874 | /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, |
| 875 | /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, |
| 876 | /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, |
| 877 | /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, |
| 878 | /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, |
| 879 | /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, |
| 880 | /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, |
| 881 | /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, |
| 882 | /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, |
| 883 | /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, |
| 884 | /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, |
| 885 | /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, |
| 886 | /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, |
| 887 | /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, |
| 888 | /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, |
| 889 | /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, |
| 890 | /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 891 | /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, |
| 892 | /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, |
| 893 | /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, |
| 894 | /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, |
| 895 | /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 896 | /* 1310 */ 445, 445, 152, |
| 897 | }; |
| 898 | static const YYCODETYPE yy_lookahead[] = { |
| 899 | /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, |
| 900 | /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, |
| 901 | /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, |
| 902 | /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, |
| 903 | /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, |
| 904 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 905 | /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, |
| 906 | /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, |
| 907 | /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, |
| 908 | /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, |
| 909 | /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, |
| 910 | /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, |
| 911 | /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, |
| 912 | /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, |
| 913 | /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, |
| 914 | /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, |
| 915 | /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, |
| 916 | /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, |
| 917 | /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, |
| 918 | /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, |
| 919 | /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, |
| 920 | /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, |
| 921 | /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, |
| 922 | /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, |
| 923 | /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, |
| 924 | /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, |
| 925 | /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, |
| 926 | /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, |
| 927 | /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, |
| 928 | /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, |
| 929 | /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, |
| 930 | /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, |
| 931 | /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, |
| 932 | /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, |
| 933 | /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, |
| 934 | /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, |
| 935 | /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, |
| 936 | /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, |
| 937 | /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, |
| 938 | /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, |
| 939 | /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, |
| 940 | /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, |
| 941 | /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, |
| 942 | /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, |
| 943 | /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, |
| 944 | /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, |
| 945 | /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, |
| 946 | /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, |
| 947 | /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, |
| 948 | /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, |
| 949 | /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, |
| 950 | /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, |
| 951 | /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, |
| 952 | /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, |
| 953 | /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, |
| 954 | /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, |
| 955 | /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, |
| 956 | /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, |
| 957 | /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, |
| 958 | /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, |
| 959 | /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, |
| 960 | /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, |
| 961 | /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, |
| 962 | /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, |
| 963 | /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, |
| 964 | /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, |
| 965 | /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, |
| 966 | /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, |
| 967 | /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, |
| 968 | /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, |
| 969 | /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, |
| 970 | /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, |
| 971 | /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, |
| 972 | /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, |
| 973 | /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, |
| 974 | /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, |
| 975 | /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, |
| 976 | /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, |
| 977 | /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, |
| 978 | /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, |
| 979 | /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, |
| 980 | /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, |
| 981 | /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, |
| 982 | /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, |
| 983 | /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, |
| 984 | /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, |
| 985 | /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, |
| 986 | /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, |
| 987 | /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, |
| 988 | /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, |
| 989 | /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, |
| 990 | /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, |
| 991 | /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, |
| 992 | /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, |
| 993 | /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, |
| 994 | /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, |
| 995 | /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, |
| 996 | /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, |
| 997 | /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, |
| 998 | /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, |
| 999 | /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, |
| 1000 | /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, |
| 1001 | /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, |
| 1002 | /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, |
| 1003 | /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, |
| 1004 | /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, |
| 1005 | /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, |
| 1006 | /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, |
| 1007 | /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, |
| 1008 | /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, |
| 1009 | /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, |
| 1010 | /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, |
| 1011 | /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, |
| 1012 | /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, |
| 1013 | /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, |
| 1014 | /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, |
| 1015 | /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, |
| 1016 | /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, |
| 1017 | /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, |
| 1018 | /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, |
| 1019 | /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, |
| 1020 | /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, |
| 1021 | /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, |
| 1022 | /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, |
| 1023 | /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, |
| 1024 | /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1025 | /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, |
| 1026 | /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, |
| 1027 | /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1028 | /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, |
| 1029 | /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1030 | /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, |
| 1031 | /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1032 | /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1033 | /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1034 | /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1035 | /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1036 | /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1037 | /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1038 | /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1039 | /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1040 | /* 1410 */ 100, 100, 100, |
| 1041 | }; |
| 1042 | #define YY_SHIFT_COUNT (163) |
| 1043 | #define YY_SHIFT_MIN (0) |
| 1044 | #define YY_SHIFT_MAX (1113) |
| 1045 | static const unsigned short int yy_shift_ofst[] = { |
| 1046 | /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, |
| 1047 | /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, |
| 1048 | /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, |
| 1049 | /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, |
| 1050 | /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, |
| 1051 | /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, |
| 1052 | /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, |
| 1053 | /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, |
| 1054 | /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, |
| 1055 | /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, |
| 1056 | /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, |
| 1057 | /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, |
| 1058 | /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, |
| 1059 | /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, |
| 1060 | /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, |
| 1061 | /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, |
| 1062 | /* 160 */ 363, 293, 374, 373, |
| 1063 | }; |
| 1064 | #define YY_REDUCE_COUNT (82) |
| 1065 | #define YY_REDUCE_MIN (-130) |
| 1066 | #define YY_REDUCE_MAX (1177) |
| 1067 | static const short yy_reduce_ofst[] = { |
| 1068 | /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, |
| 1069 | /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, |
| 1070 | /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, |
| 1071 | /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, |
| 1072 | /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, |
| 1073 | /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, |
| 1074 | /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, |
| 1075 | /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, |
| 1076 | /* 80 */ 46, 52, -36, |
| 1077 | }; |
| 1078 | static const YYACTIONTYPE yy_default[] = { |
| 1079 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1080 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1081 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1177,10 +1181,11 @@ | |
| 1181 | 0, /* RJUST => nothing */ |
| 1182 | 0, /* ABOVE => nothing */ |
| 1183 | 0, /* BELOW => nothing */ |
| 1184 | 0, /* ITALIC => nothing */ |
| 1185 | 0, /* BOLD => nothing */ |
| 1186 | 0, /* MONO => nothing */ |
| 1187 | 0, /* ALIGNED => nothing */ |
| 1188 | 0, /* BIG => nothing */ |
| 1189 | 0, /* SMALL => nothing */ |
| 1190 | 0, /* AND => nothing */ |
| 1191 | 0, /* LT => nothing */ |
| @@ -1364,74 +1369,75 @@ | |
| 1369 | /* 66 */ "RJUST", |
| 1370 | /* 67 */ "ABOVE", |
| 1371 | /* 68 */ "BELOW", |
| 1372 | /* 69 */ "ITALIC", |
| 1373 | /* 70 */ "BOLD", |
| 1374 | /* 71 */ "MONO", |
| 1375 | /* 72 */ "ALIGNED", |
| 1376 | /* 73 */ "BIG", |
| 1377 | /* 74 */ "SMALL", |
| 1378 | /* 75 */ "AND", |
| 1379 | /* 76 */ "LT", |
| 1380 | /* 77 */ "GT", |
| 1381 | /* 78 */ "ON", |
| 1382 | /* 79 */ "WAY", |
| 1383 | /* 80 */ "BETWEEN", |
| 1384 | /* 81 */ "THE", |
| 1385 | /* 82 */ "NTH", |
| 1386 | /* 83 */ "VERTEX", |
| 1387 | /* 84 */ "TOP", |
| 1388 | /* 85 */ "BOTTOM", |
| 1389 | /* 86 */ "START", |
| 1390 | /* 87 */ "END", |
| 1391 | /* 88 */ "IN", |
| 1392 | /* 89 */ "THIS", |
| 1393 | /* 90 */ "DOT_U", |
| 1394 | /* 91 */ "LAST", |
| 1395 | /* 92 */ "NUMBER", |
| 1396 | /* 93 */ "FUNC1", |
| 1397 | /* 94 */ "FUNC2", |
| 1398 | /* 95 */ "DIST", |
| 1399 | /* 96 */ "DOT_XY", |
| 1400 | /* 97 */ "X", |
| 1401 | /* 98 */ "Y", |
| 1402 | /* 99 */ "DOT_L", |
| 1403 | /* 100 */ "statement_list", |
| 1404 | /* 101 */ "statement", |
| 1405 | /* 102 */ "unnamed_statement", |
| 1406 | /* 103 */ "basetype", |
| 1407 | /* 104 */ "expr", |
| 1408 | /* 105 */ "numproperty", |
| 1409 | /* 106 */ "edge", |
| 1410 | /* 107 */ "direction", |
| 1411 | /* 108 */ "dashproperty", |
| 1412 | /* 109 */ "colorproperty", |
| 1413 | /* 110 */ "locproperty", |
| 1414 | /* 111 */ "position", |
| 1415 | /* 112 */ "place", |
| 1416 | /* 113 */ "object", |
| 1417 | /* 114 */ "objectname", |
| 1418 | /* 115 */ "nth", |
| 1419 | /* 116 */ "textposition", |
| 1420 | /* 117 */ "rvalue", |
| 1421 | /* 118 */ "lvalue", |
| 1422 | /* 119 */ "even", |
| 1423 | /* 120 */ "relexpr", |
| 1424 | /* 121 */ "optrelexpr", |
| 1425 | /* 122 */ "document", |
| 1426 | /* 123 */ "print", |
| 1427 | /* 124 */ "prlist", |
| 1428 | /* 125 */ "pritem", |
| 1429 | /* 126 */ "prsep", |
| 1430 | /* 127 */ "attribute_list", |
| 1431 | /* 128 */ "savelist", |
| 1432 | /* 129 */ "alist", |
| 1433 | /* 130 */ "attribute", |
| 1434 | /* 131 */ "go", |
| 1435 | /* 132 */ "boolproperty", |
| 1436 | /* 133 */ "withclause", |
| 1437 | /* 134 */ "between", |
| 1438 | /* 135 */ "place2", |
| 1439 | }; |
| 1440 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1441 | |
| 1442 | #ifndef NDEBUG |
| 1443 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1498,11 +1504,11 @@ | |
| 1504 | /* 58 */ "boolproperty ::= INVIS", |
| 1505 | /* 59 */ "boolproperty ::= THICK", |
| 1506 | /* 60 */ "boolproperty ::= THIN", |
| 1507 | /* 61 */ "boolproperty ::= SOLID", |
| 1508 | /* 62 */ "textposition ::=", |
| 1509 | /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", |
| 1510 | /* 64 */ "position ::= expr COMMA expr", |
| 1511 | /* 65 */ "position ::= place PLUS expr COMMA expr", |
| 1512 | /* 66 */ "position ::= place MINUS expr COMMA expr", |
| 1513 | /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", |
| 1514 | /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", |
| @@ -1716,24 +1722,24 @@ | |
| 1722 | ** Note: during a reduce, the only symbols destroyed are those |
| 1723 | ** which appear on the RHS of the rule, but which are *not* used |
| 1724 | ** inside the C code. |
| 1725 | */ |
| 1726 | /********* Begin destructor definitions ***************************************/ |
| 1727 | case 100: /* statement_list */ |
| 1728 | { |
| 1729 | #line 510 "pikchr.y" |
| 1730 | pik_elist_free(p,(yypminor->yy235)); |
| 1731 | #line 1756 "pikchr.c" |
| 1732 | } |
| 1733 | break; |
| 1734 | case 101: /* statement */ |
| 1735 | case 102: /* unnamed_statement */ |
| 1736 | case 103: /* basetype */ |
| 1737 | { |
| 1738 | #line 512 "pikchr.y" |
| 1739 | pik_elem_free(p,(yypminor->yy162)); |
| 1740 | #line 1765 "pikchr.c" |
| 1741 | } |
| 1742 | break; |
| 1743 | /********* End destructor definitions *****************************************/ |
| 1744 | default: break; /* If no destructor action specified: do nothing */ |
| 1745 | } |
| @@ -1947,14 +1953,14 @@ | |
| 1953 | #endif |
| 1954 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 1955 | /* Here code is inserted which will execute if the parser |
| 1956 | ** stack every overflows */ |
| 1957 | /******** Begin %stack_overflow code ******************************************/ |
| 1958 | #line 544 "pikchr.y" |
| 1959 | |
| 1960 | pik_error(p, 0, "parser stack overflow"); |
| 1961 | #line 1986 "pikchr.c" |
| 1962 | /******** End %stack_overflow code ********************************************/ |
| 1963 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 1964 | pik_parserCTX_STORE |
| 1965 | } |
| 1966 | |
| @@ -2022,166 +2028,166 @@ | |
| 2028 | } |
| 2029 | |
| 2030 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2031 | ** of that rule */ |
| 2032 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2033 | 122, /* (0) document ::= statement_list */ |
| 2034 | 100, /* (1) statement_list ::= statement */ |
| 2035 | 100, /* (2) statement_list ::= statement_list EOL statement */ |
| 2036 | 101, /* (3) statement ::= */ |
| 2037 | 101, /* (4) statement ::= direction */ |
| 2038 | 101, /* (5) statement ::= lvalue ASSIGN rvalue */ |
| 2039 | 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ |
| 2040 | 101, /* (7) statement ::= PLACENAME COLON position */ |
| 2041 | 101, /* (8) statement ::= unnamed_statement */ |
| 2042 | 101, /* (9) statement ::= print prlist */ |
| 2043 | 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ |
| 2044 | 101, /* (11) statement ::= ASSERT LP position EQ position RP */ |
| 2045 | 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ |
| 2046 | 117, /* (13) rvalue ::= PLACENAME */ |
| 2047 | 125, /* (14) pritem ::= FILL */ |
| 2048 | 125, /* (15) pritem ::= COLOR */ |
| 2049 | 125, /* (16) pritem ::= THICKNESS */ |
| 2050 | 125, /* (17) pritem ::= rvalue */ |
| 2051 | 125, /* (18) pritem ::= STRING */ |
| 2052 | 126, /* (19) prsep ::= COMMA */ |
| 2053 | 102, /* (20) unnamed_statement ::= basetype attribute_list */ |
| 2054 | 103, /* (21) basetype ::= CLASSNAME */ |
| 2055 | 103, /* (22) basetype ::= STRING textposition */ |
| 2056 | 103, /* (23) basetype ::= LB savelist statement_list RB */ |
| 2057 | 128, /* (24) savelist ::= */ |
| 2058 | 120, /* (25) relexpr ::= expr */ |
| 2059 | 120, /* (26) relexpr ::= expr PERCENT */ |
| 2060 | 121, /* (27) optrelexpr ::= */ |
| 2061 | 127, /* (28) attribute_list ::= relexpr alist */ |
| 2062 | 130, /* (29) attribute ::= numproperty relexpr */ |
| 2063 | 130, /* (30) attribute ::= dashproperty expr */ |
| 2064 | 130, /* (31) attribute ::= dashproperty */ |
| 2065 | 130, /* (32) attribute ::= colorproperty rvalue */ |
| 2066 | 130, /* (33) attribute ::= go direction optrelexpr */ |
| 2067 | 130, /* (34) attribute ::= go direction even position */ |
| 2068 | 130, /* (35) attribute ::= CLOSE */ |
| 2069 | 130, /* (36) attribute ::= CHOP */ |
| 2070 | 130, /* (37) attribute ::= FROM position */ |
| 2071 | 130, /* (38) attribute ::= TO position */ |
| 2072 | 130, /* (39) attribute ::= THEN */ |
| 2073 | 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ |
| 2074 | 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ |
| 2075 | 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ |
| 2076 | 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ |
| 2077 | 130, /* (44) attribute ::= AT position */ |
| 2078 | 130, /* (45) attribute ::= SAME */ |
| 2079 | 130, /* (46) attribute ::= SAME AS object */ |
| 2080 | 130, /* (47) attribute ::= STRING textposition */ |
| 2081 | 130, /* (48) attribute ::= FIT */ |
| 2082 | 130, /* (49) attribute ::= BEHIND object */ |
| 2083 | 133, /* (50) withclause ::= DOT_E edge AT position */ |
| 2084 | 133, /* (51) withclause ::= edge AT position */ |
| 2085 | 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2086 | 132, /* (53) boolproperty ::= CW */ |
| 2087 | 132, /* (54) boolproperty ::= CCW */ |
| 2088 | 132, /* (55) boolproperty ::= LARROW */ |
| 2089 | 132, /* (56) boolproperty ::= RARROW */ |
| 2090 | 132, /* (57) boolproperty ::= LRARROW */ |
| 2091 | 132, /* (58) boolproperty ::= INVIS */ |
| 2092 | 132, /* (59) boolproperty ::= THICK */ |
| 2093 | 132, /* (60) boolproperty ::= THIN */ |
| 2094 | 132, /* (61) boolproperty ::= SOLID */ |
| 2095 | 116, /* (62) textposition ::= */ |
| 2096 | 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2097 | 111, /* (64) position ::= expr COMMA expr */ |
| 2098 | 111, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2099 | 111, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2100 | 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2101 | 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| 2102 | 111, /* (69) position ::= LP position COMMA position RP */ |
| 2103 | 111, /* (70) position ::= LP position RP */ |
| 2104 | 111, /* (71) position ::= expr between position AND position */ |
| 2105 | 111, /* (72) position ::= expr LT position COMMA position GT */ |
| 2106 | 111, /* (73) position ::= expr ABOVE position */ |
| 2107 | 111, /* (74) position ::= expr BELOW position */ |
| 2108 | 111, /* (75) position ::= expr LEFT OF position */ |
| 2109 | 111, /* (76) position ::= expr RIGHT OF position */ |
| 2110 | 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ |
| 2111 | 111, /* (78) position ::= expr HEADING EDGEPT OF position */ |
| 2112 | 111, /* (79) position ::= expr EDGEPT OF position */ |
| 2113 | 111, /* (80) position ::= expr ON HEADING expr FROM position */ |
| 2114 | 111, /* (81) position ::= expr HEADING expr FROM position */ |
| 2115 | 112, /* (82) place ::= edge OF object */ |
| 2116 | 135, /* (83) place2 ::= object */ |
| 2117 | 135, /* (84) place2 ::= object DOT_E edge */ |
| 2118 | 135, /* (85) place2 ::= NTH VERTEX OF object */ |
| 2119 | 113, /* (86) object ::= nth */ |
| 2120 | 113, /* (87) object ::= nth OF|IN object */ |
| 2121 | 114, /* (88) objectname ::= THIS */ |
| 2122 | 114, /* (89) objectname ::= PLACENAME */ |
| 2123 | 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ |
| 2124 | 115, /* (91) nth ::= NTH CLASSNAME */ |
| 2125 | 115, /* (92) nth ::= NTH LAST CLASSNAME */ |
| 2126 | 115, /* (93) nth ::= LAST CLASSNAME */ |
| 2127 | 115, /* (94) nth ::= LAST */ |
| 2128 | 115, /* (95) nth ::= NTH LB RB */ |
| 2129 | 115, /* (96) nth ::= NTH LAST LB RB */ |
| 2130 | 115, /* (97) nth ::= LAST LB RB */ |
| 2131 | 104, /* (98) expr ::= expr PLUS expr */ |
| 2132 | 104, /* (99) expr ::= expr MINUS expr */ |
| 2133 | 104, /* (100) expr ::= expr STAR expr */ |
| 2134 | 104, /* (101) expr ::= expr SLASH expr */ |
| 2135 | 104, /* (102) expr ::= MINUS expr */ |
| 2136 | 104, /* (103) expr ::= PLUS expr */ |
| 2137 | 104, /* (104) expr ::= LP expr RP */ |
| 2138 | 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2139 | 104, /* (106) expr ::= NUMBER */ |
| 2140 | 104, /* (107) expr ::= ID */ |
| 2141 | 104, /* (108) expr ::= FUNC1 LP expr RP */ |
| 2142 | 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2143 | 104, /* (110) expr ::= DIST LP position COMMA position RP */ |
| 2144 | 104, /* (111) expr ::= place2 DOT_XY X */ |
| 2145 | 104, /* (112) expr ::= place2 DOT_XY Y */ |
| 2146 | 104, /* (113) expr ::= object DOT_L numproperty */ |
| 2147 | 104, /* (114) expr ::= object DOT_L dashproperty */ |
| 2148 | 104, /* (115) expr ::= object DOT_L colorproperty */ |
| 2149 | 118, /* (116) lvalue ::= ID */ |
| 2150 | 118, /* (117) lvalue ::= FILL */ |
| 2151 | 118, /* (118) lvalue ::= COLOR */ |
| 2152 | 118, /* (119) lvalue ::= THICKNESS */ |
| 2153 | 117, /* (120) rvalue ::= expr */ |
| 2154 | 123, /* (121) print ::= PRINT */ |
| 2155 | 124, /* (122) prlist ::= pritem */ |
| 2156 | 124, /* (123) prlist ::= prlist prsep pritem */ |
| 2157 | 107, /* (124) direction ::= UP */ |
| 2158 | 107, /* (125) direction ::= DOWN */ |
| 2159 | 107, /* (126) direction ::= LEFT */ |
| 2160 | 107, /* (127) direction ::= RIGHT */ |
| 2161 | 121, /* (128) optrelexpr ::= relexpr */ |
| 2162 | 127, /* (129) attribute_list ::= alist */ |
| 2163 | 129, /* (130) alist ::= */ |
| 2164 | 129, /* (131) alist ::= alist attribute */ |
| 2165 | 130, /* (132) attribute ::= boolproperty */ |
| 2166 | 130, /* (133) attribute ::= WITH withclause */ |
| 2167 | 131, /* (134) go ::= GO */ |
| 2168 | 131, /* (135) go ::= */ |
| 2169 | 119, /* (136) even ::= UNTIL EVEN WITH */ |
| 2170 | 119, /* (137) even ::= EVEN WITH */ |
| 2171 | 108, /* (138) dashproperty ::= DOTTED */ |
| 2172 | 108, /* (139) dashproperty ::= DASHED */ |
| 2173 | 109, /* (140) colorproperty ::= FILL */ |
| 2174 | 109, /* (141) colorproperty ::= COLOR */ |
| 2175 | 111, /* (142) position ::= place */ |
| 2176 | 134, /* (143) between ::= WAY BETWEEN */ |
| 2177 | 134, /* (144) between ::= BETWEEN */ |
| 2178 | 134, /* (145) between ::= OF THE WAY BETWEEN */ |
| 2179 | 112, /* (146) place ::= place2 */ |
| 2180 | 106, /* (147) edge ::= CENTER */ |
| 2181 | 106, /* (148) edge ::= EDGEPT */ |
| 2182 | 106, /* (149) edge ::= TOP */ |
| 2183 | 106, /* (150) edge ::= BOTTOM */ |
| 2184 | 106, /* (151) edge ::= START */ |
| 2185 | 106, /* (152) edge ::= END */ |
| 2186 | 106, /* (153) edge ::= RIGHT */ |
| 2187 | 106, /* (154) edge ::= LEFT */ |
| 2188 | 113, /* (155) object ::= objectname */ |
| 2189 | }; |
| 2190 | |
| 2191 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2192 | ** of symbols on the right-hand side of that rule. */ |
| 2193 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2246,11 +2252,11 @@ | |
| 2252 | -1, /* (58) boolproperty ::= INVIS */ |
| 2253 | -1, /* (59) boolproperty ::= THICK */ |
| 2254 | -1, /* (60) boolproperty ::= THIN */ |
| 2255 | -1, /* (61) boolproperty ::= SOLID */ |
| 2256 | 0, /* (62) textposition ::= */ |
| 2257 | -2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2258 | -3, /* (64) position ::= expr COMMA expr */ |
| 2259 | -5, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2260 | -5, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2261 | -7, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2262 | -7, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| @@ -2381,620 +2387,620 @@ | |
| 2387 | ** break; |
| 2388 | */ |
| 2389 | /********** Begin reduce actions **********************************************/ |
| 2390 | YYMINORTYPE yylhsminor; |
| 2391 | case 0: /* document ::= statement_list */ |
| 2392 | #line 548 "pikchr.y" |
| 2393 | {pik_render(p,yymsp[0].minor.yy235);} |
| 2394 | #line 2419 "pikchr.c" |
| 2395 | break; |
| 2396 | case 1: /* statement_list ::= statement */ |
| 2397 | #line 551 "pikchr.y" |
| 2398 | { yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } |
| 2399 | #line 2424 "pikchr.c" |
| 2400 | yymsp[0].minor.yy235 = yylhsminor.yy235; |
| 2401 | break; |
| 2402 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2403 | #line 553 "pikchr.y" |
| 2404 | { yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } |
| 2405 | #line 2430 "pikchr.c" |
| 2406 | yymsp[-2].minor.yy235 = yylhsminor.yy235; |
| 2407 | break; |
| 2408 | case 3: /* statement ::= */ |
| 2409 | #line 556 "pikchr.y" |
| 2410 | { yymsp[1].minor.yy162 = 0; } |
| 2411 | #line 2436 "pikchr.c" |
| 2412 | break; |
| 2413 | case 4: /* statement ::= direction */ |
| 2414 | #line 557 "pikchr.y" |
| 2415 | { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } |
| 2416 | #line 2441 "pikchr.c" |
| 2417 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2418 | break; |
| 2419 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2420 | #line 558 "pikchr.y" |
| 2421 | {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} |
| 2422 | #line 2447 "pikchr.c" |
| 2423 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2424 | break; |
| 2425 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2426 | #line 560 "pikchr.y" |
| 2427 | { yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } |
| 2428 | #line 2453 "pikchr.c" |
| 2429 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2430 | break; |
| 2431 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2432 | #line 562 "pikchr.y" |
| 2433 | { yylhsminor.yy162 = pik_elem_new(p,0,0,0); |
| 2434 | if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} |
| 2435 | #line 2460 "pikchr.c" |
| 2436 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2437 | break; |
| 2438 | case 8: /* statement ::= unnamed_statement */ |
| 2439 | #line 564 "pikchr.y" |
| 2440 | {yylhsminor.yy162 = yymsp[0].minor.yy162;} |
| 2441 | #line 2466 "pikchr.c" |
| 2442 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2443 | break; |
| 2444 | case 9: /* statement ::= print prlist */ |
| 2445 | #line 565 "pikchr.y" |
| 2446 | {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;} |
| 2447 | #line 2472 "pikchr.c" |
| 2448 | break; |
| 2449 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2450 | #line 570 "pikchr.y" |
| 2451 | {yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} |
| 2452 | #line 2477 "pikchr.c" |
| 2453 | break; |
| 2454 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2455 | #line 572 "pikchr.y" |
| 2456 | {yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} |
| 2457 | #line 2482 "pikchr.c" |
| 2458 | break; |
| 2459 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2460 | #line 573 "pikchr.y" |
| 2461 | {yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} |
| 2462 | #line 2487 "pikchr.c" |
| 2463 | break; |
| 2464 | case 13: /* rvalue ::= PLACENAME */ |
| 2465 | #line 584 "pikchr.y" |
| 2466 | {yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} |
| 2467 | #line 2492 "pikchr.c" |
| 2468 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2469 | break; |
| 2470 | case 14: /* pritem ::= FILL */ |
| 2471 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2472 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2473 | #line 589 "pikchr.y" |
| 2474 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2475 | #line 2500 "pikchr.c" |
| 2476 | break; |
| 2477 | case 17: /* pritem ::= rvalue */ |
| 2478 | #line 592 "pikchr.y" |
| 2479 | {pik_append_num(p,"",yymsp[0].minor.yy21);} |
| 2480 | #line 2505 "pikchr.c" |
| 2481 | break; |
| 2482 | case 18: /* pritem ::= STRING */ |
| 2483 | #line 593 "pikchr.y" |
| 2484 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2485 | #line 2510 "pikchr.c" |
| 2486 | break; |
| 2487 | case 19: /* prsep ::= COMMA */ |
| 2488 | #line 594 "pikchr.y" |
| 2489 | {pik_append(p, " ", 1);} |
| 2490 | #line 2515 "pikchr.c" |
| 2491 | break; |
| 2492 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2493 | #line 597 "pikchr.y" |
| 2494 | {yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} |
| 2495 | #line 2520 "pikchr.c" |
| 2496 | yymsp[-1].minor.yy162 = yylhsminor.yy162; |
| 2497 | break; |
| 2498 | case 21: /* basetype ::= CLASSNAME */ |
| 2499 | #line 599 "pikchr.y" |
| 2500 | {yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } |
| 2501 | #line 2526 "pikchr.c" |
| 2502 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2503 | break; |
| 2504 | case 22: /* basetype ::= STRING textposition */ |
| 2505 | #line 601 "pikchr.y" |
| 2506 | {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } |
| 2507 | #line 2532 "pikchr.c" |
| 2508 | yymsp[-1].minor.yy162 = yylhsminor.yy162; |
| 2509 | break; |
| 2510 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2511 | #line 603 "pikchr.y" |
| 2512 | { p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } |
| 2513 | #line 2538 "pikchr.c" |
| 2514 | break; |
| 2515 | case 24: /* savelist ::= */ |
| 2516 | #line 608 "pikchr.y" |
| 2517 | {yymsp[1].minor.yy235 = p->list; p->list = 0;} |
| 2518 | #line 2543 "pikchr.c" |
| 2519 | break; |
| 2520 | case 25: /* relexpr ::= expr */ |
| 2521 | #line 615 "pikchr.y" |
| 2522 | {yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} |
| 2523 | #line 2548 "pikchr.c" |
| 2524 | yymsp[0].minor.yy72 = yylhsminor.yy72; |
| 2525 | break; |
| 2526 | case 26: /* relexpr ::= expr PERCENT */ |
| 2527 | #line 616 "pikchr.y" |
| 2528 | {yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} |
| 2529 | #line 2554 "pikchr.c" |
| 2530 | yymsp[-1].minor.yy72 = yylhsminor.yy72; |
| 2531 | break; |
| 2532 | case 27: /* optrelexpr ::= */ |
| 2533 | #line 618 "pikchr.y" |
| 2534 | {yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} |
| 2535 | #line 2560 "pikchr.c" |
| 2536 | break; |
| 2537 | case 28: /* attribute_list ::= relexpr alist */ |
| 2538 | #line 620 "pikchr.y" |
| 2539 | {pik_add_direction(p,0,&yymsp[-1].minor.yy72);} |
| 2540 | #line 2565 "pikchr.c" |
| 2541 | break; |
| 2542 | case 29: /* attribute ::= numproperty relexpr */ |
| 2543 | #line 624 "pikchr.y" |
| 2544 | { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } |
| 2545 | #line 2570 "pikchr.c" |
| 2546 | break; |
| 2547 | case 30: /* attribute ::= dashproperty expr */ |
| 2548 | #line 625 "pikchr.y" |
| 2549 | { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } |
| 2550 | #line 2575 "pikchr.c" |
| 2551 | break; |
| 2552 | case 31: /* attribute ::= dashproperty */ |
| 2553 | #line 626 "pikchr.y" |
| 2554 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2555 | #line 2580 "pikchr.c" |
| 2556 | break; |
| 2557 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2558 | #line 627 "pikchr.y" |
| 2559 | { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } |
| 2560 | #line 2585 "pikchr.c" |
| 2561 | break; |
| 2562 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2563 | #line 628 "pikchr.y" |
| 2564 | { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} |
| 2565 | #line 2590 "pikchr.c" |
| 2566 | break; |
| 2567 | case 34: /* attribute ::= go direction even position */ |
| 2568 | #line 629 "pikchr.y" |
| 2569 | {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} |
| 2570 | #line 2595 "pikchr.c" |
| 2571 | break; |
| 2572 | case 35: /* attribute ::= CLOSE */ |
| 2573 | #line 630 "pikchr.y" |
| 2574 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2575 | #line 2600 "pikchr.c" |
| 2576 | break; |
| 2577 | case 36: /* attribute ::= CHOP */ |
| 2578 | #line 631 "pikchr.y" |
| 2579 | { p->cur->bChop = 1; } |
| 2580 | #line 2605 "pikchr.c" |
| 2581 | break; |
| 2582 | case 37: /* attribute ::= FROM position */ |
| 2583 | #line 632 "pikchr.y" |
| 2584 | { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } |
| 2585 | #line 2610 "pikchr.c" |
| 2586 | break; |
| 2587 | case 38: /* attribute ::= TO position */ |
| 2588 | #line 633 "pikchr.y" |
| 2589 | { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } |
| 2590 | #line 2615 "pikchr.c" |
| 2591 | break; |
| 2592 | case 39: /* attribute ::= THEN */ |
| 2593 | #line 634 "pikchr.y" |
| 2594 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2595 | #line 2620 "pikchr.c" |
| 2596 | break; |
| 2597 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2598 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2599 | #line 636 "pikchr.y" |
| 2600 | {pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} |
| 2601 | #line 2626 "pikchr.c" |
| 2602 | break; |
| 2603 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2604 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2605 | #line 637 "pikchr.y" |
| 2606 | {pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} |
| 2607 | #line 2632 "pikchr.c" |
| 2608 | break; |
| 2609 | case 44: /* attribute ::= AT position */ |
| 2610 | #line 642 "pikchr.y" |
| 2611 | { pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } |
| 2612 | #line 2637 "pikchr.c" |
| 2613 | break; |
| 2614 | case 45: /* attribute ::= SAME */ |
| 2615 | #line 644 "pikchr.y" |
| 2616 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2617 | #line 2642 "pikchr.c" |
| 2618 | break; |
| 2619 | case 46: /* attribute ::= SAME AS object */ |
| 2620 | #line 645 "pikchr.y" |
| 2621 | {pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2622 | #line 2647 "pikchr.c" |
| 2623 | break; |
| 2624 | case 47: /* attribute ::= STRING textposition */ |
| 2625 | #line 646 "pikchr.y" |
| 2626 | {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} |
| 2627 | #line 2652 "pikchr.c" |
| 2628 | break; |
| 2629 | case 48: /* attribute ::= FIT */ |
| 2630 | #line 647 "pikchr.y" |
| 2631 | {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } |
| 2632 | #line 2657 "pikchr.c" |
| 2633 | break; |
| 2634 | case 49: /* attribute ::= BEHIND object */ |
| 2635 | #line 648 "pikchr.y" |
| 2636 | {pik_behind(p,yymsp[0].minor.yy162);} |
| 2637 | #line 2662 "pikchr.c" |
| 2638 | break; |
| 2639 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2640 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2641 | #line 656 "pikchr.y" |
| 2642 | { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } |
| 2643 | #line 2668 "pikchr.c" |
| 2644 | break; |
| 2645 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2646 | #line 660 "pikchr.y" |
| 2647 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2648 | #line 2673 "pikchr.c" |
| 2649 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2650 | break; |
| 2651 | case 53: /* boolproperty ::= CW */ |
| 2652 | #line 671 "pikchr.y" |
| 2653 | {p->cur->cw = 1;} |
| 2654 | #line 2679 "pikchr.c" |
| 2655 | break; |
| 2656 | case 54: /* boolproperty ::= CCW */ |
| 2657 | #line 672 "pikchr.y" |
| 2658 | {p->cur->cw = 0;} |
| 2659 | #line 2684 "pikchr.c" |
| 2660 | break; |
| 2661 | case 55: /* boolproperty ::= LARROW */ |
| 2662 | #line 673 "pikchr.y" |
| 2663 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2664 | #line 2689 "pikchr.c" |
| 2665 | break; |
| 2666 | case 56: /* boolproperty ::= RARROW */ |
| 2667 | #line 674 "pikchr.y" |
| 2668 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2669 | #line 2694 "pikchr.c" |
| 2670 | break; |
| 2671 | case 57: /* boolproperty ::= LRARROW */ |
| 2672 | #line 675 "pikchr.y" |
| 2673 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2674 | #line 2699 "pikchr.c" |
| 2675 | break; |
| 2676 | case 58: /* boolproperty ::= INVIS */ |
| 2677 | #line 676 "pikchr.y" |
| 2678 | {p->cur->sw = 0.0;} |
| 2679 | #line 2704 "pikchr.c" |
| 2680 | break; |
| 2681 | case 59: /* boolproperty ::= THICK */ |
| 2682 | #line 677 "pikchr.y" |
| 2683 | {p->cur->sw *= 1.5;} |
| 2684 | #line 2709 "pikchr.c" |
| 2685 | break; |
| 2686 | case 60: /* boolproperty ::= THIN */ |
| 2687 | #line 678 "pikchr.y" |
| 2688 | {p->cur->sw *= 0.67;} |
| 2689 | #line 2714 "pikchr.c" |
| 2690 | break; |
| 2691 | case 61: /* boolproperty ::= SOLID */ |
| 2692 | #line 679 "pikchr.y" |
| 2693 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2694 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2695 | #line 2720 "pikchr.c" |
| 2696 | break; |
| 2697 | case 62: /* textposition ::= */ |
| 2698 | #line 682 "pikchr.y" |
| 2699 | {yymsp[1].minor.yy188 = 0;} |
| 2700 | #line 2725 "pikchr.c" |
| 2701 | break; |
| 2702 | case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2703 | #line 685 "pikchr.y" |
| 2704 | {yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} |
| 2705 | #line 2730 "pikchr.c" |
| 2706 | yymsp[-1].minor.yy188 = yylhsminor.yy188; |
| 2707 | break; |
| 2708 | case 64: /* position ::= expr COMMA expr */ |
| 2709 | #line 688 "pikchr.y" |
| 2710 | {yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} |
| 2711 | #line 2736 "pikchr.c" |
| 2712 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2713 | break; |
| 2714 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2715 | #line 690 "pikchr.y" |
| 2716 | {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} |
| 2717 | #line 2742 "pikchr.c" |
| 2718 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2719 | break; |
| 2720 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2721 | #line 691 "pikchr.y" |
| 2722 | {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} |
| 2723 | #line 2748 "pikchr.c" |
| 2724 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2725 | break; |
| 2726 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2727 | #line 693 "pikchr.y" |
| 2728 | {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} |
| 2729 | #line 2754 "pikchr.c" |
| 2730 | yymsp[-6].minor.yy63 = yylhsminor.yy63; |
| 2731 | break; |
| 2732 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2733 | #line 695 "pikchr.y" |
| 2734 | {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} |
| 2735 | #line 2760 "pikchr.c" |
| 2736 | yymsp[-6].minor.yy63 = yylhsminor.yy63; |
| 2737 | break; |
| 2738 | case 69: /* position ::= LP position COMMA position RP */ |
| 2739 | #line 696 "pikchr.y" |
| 2740 | {yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} |
| 2741 | #line 2766 "pikchr.c" |
| 2742 | break; |
| 2743 | case 70: /* position ::= LP position RP */ |
| 2744 | #line 697 "pikchr.y" |
| 2745 | {yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} |
| 2746 | #line 2771 "pikchr.c" |
| 2747 | break; |
| 2748 | case 71: /* position ::= expr between position AND position */ |
| 2749 | #line 699 "pikchr.y" |
| 2750 | {yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} |
| 2751 | #line 2776 "pikchr.c" |
| 2752 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2753 | break; |
| 2754 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2755 | #line 701 "pikchr.y" |
| 2756 | {yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} |
| 2757 | #line 2782 "pikchr.c" |
| 2758 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2759 | break; |
| 2760 | case 73: /* position ::= expr ABOVE position */ |
| 2761 | #line 702 "pikchr.y" |
| 2762 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} |
| 2763 | #line 2788 "pikchr.c" |
| 2764 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2765 | break; |
| 2766 | case 74: /* position ::= expr BELOW position */ |
| 2767 | #line 703 "pikchr.y" |
| 2768 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} |
| 2769 | #line 2794 "pikchr.c" |
| 2770 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2771 | break; |
| 2772 | case 75: /* position ::= expr LEFT OF position */ |
| 2773 | #line 704 "pikchr.y" |
| 2774 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} |
| 2775 | #line 2800 "pikchr.c" |
| 2776 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2777 | break; |
| 2778 | case 76: /* position ::= expr RIGHT OF position */ |
| 2779 | #line 705 "pikchr.y" |
| 2780 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} |
| 2781 | #line 2806 "pikchr.c" |
| 2782 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2783 | break; |
| 2784 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2785 | #line 707 "pikchr.y" |
| 2786 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2787 | #line 2812 "pikchr.c" |
| 2788 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2789 | break; |
| 2790 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2791 | #line 709 "pikchr.y" |
| 2792 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2793 | #line 2818 "pikchr.c" |
| 2794 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2795 | break; |
| 2796 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2797 | #line 711 "pikchr.y" |
| 2798 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2799 | #line 2824 "pikchr.c" |
| 2800 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2801 | break; |
| 2802 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2803 | #line 713 "pikchr.y" |
| 2804 | {yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} |
| 2805 | #line 2830 "pikchr.c" |
| 2806 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2807 | break; |
| 2808 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2809 | #line 715 "pikchr.y" |
| 2810 | {yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} |
| 2811 | #line 2836 "pikchr.c" |
| 2812 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2813 | break; |
| 2814 | case 82: /* place ::= edge OF object */ |
| 2815 | #line 727 "pikchr.y" |
| 2816 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2817 | #line 2842 "pikchr.c" |
| 2818 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2819 | break; |
| 2820 | case 83: /* place2 ::= object */ |
| 2821 | #line 728 "pikchr.y" |
| 2822 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} |
| 2823 | #line 2848 "pikchr.c" |
| 2824 | yymsp[0].minor.yy63 = yylhsminor.yy63; |
| 2825 | break; |
| 2826 | case 84: /* place2 ::= object DOT_E edge */ |
| 2827 | #line 729 "pikchr.y" |
| 2828 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 2829 | #line 2854 "pikchr.c" |
| 2830 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2831 | break; |
| 2832 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2833 | #line 730 "pikchr.y" |
| 2834 | {yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} |
| 2835 | #line 2860 "pikchr.c" |
| 2836 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2837 | break; |
| 2838 | case 86: /* object ::= nth */ |
| 2839 | #line 742 "pikchr.y" |
| 2840 | {yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} |
| 2841 | #line 2866 "pikchr.c" |
| 2842 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2843 | break; |
| 2844 | case 87: /* object ::= nth OF|IN object */ |
| 2845 | #line 743 "pikchr.y" |
| 2846 | {yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2847 | #line 2872 "pikchr.c" |
| 2848 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2849 | break; |
| 2850 | case 88: /* objectname ::= THIS */ |
| 2851 | #line 745 "pikchr.y" |
| 2852 | {yymsp[0].minor.yy162 = p->cur;} |
| 2853 | #line 2878 "pikchr.c" |
| 2854 | break; |
| 2855 | case 89: /* objectname ::= PLACENAME */ |
| 2856 | #line 746 "pikchr.y" |
| 2857 | {yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} |
| 2858 | #line 2883 "pikchr.c" |
| 2859 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2860 | break; |
| 2861 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2862 | #line 748 "pikchr.y" |
| 2863 | {yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 2864 | #line 2889 "pikchr.c" |
| 2865 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2866 | break; |
| 2867 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2868 | #line 750 "pikchr.y" |
| 2869 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2870 | #line 2895 "pikchr.c" |
| 2871 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2872 | break; |
| 2873 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2874 | #line 751 "pikchr.y" |
| 2875 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2876 | #line 2901 "pikchr.c" |
| 2877 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2878 | break; |
| 2879 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2880 | #line 752 "pikchr.y" |
| 2881 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2882 | #line 2907 "pikchr.c" |
| 2883 | break; |
| 2884 | case 94: /* nth ::= LAST */ |
| 2885 | #line 753 "pikchr.y" |
| 2886 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2887 | #line 2912 "pikchr.c" |
| 2888 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2889 | break; |
| 2890 | case 95: /* nth ::= NTH LB RB */ |
| 2891 | #line 754 "pikchr.y" |
| 2892 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2893 | #line 2918 "pikchr.c" |
| 2894 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2895 | break; |
| 2896 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2897 | #line 755 "pikchr.y" |
| 2898 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2899 | #line 2924 "pikchr.c" |
| 2900 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2901 | break; |
| 2902 | case 97: /* nth ::= LAST LB RB */ |
| 2903 | #line 756 "pikchr.y" |
| 2904 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2905 | #line 2930 "pikchr.c" |
| 2906 | break; |
| 2907 | case 98: /* expr ::= expr PLUS expr */ |
| 2908 | #line 758 "pikchr.y" |
| 2909 | {yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} |
| 2910 | #line 2935 "pikchr.c" |
| 2911 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2912 | break; |
| 2913 | case 99: /* expr ::= expr MINUS expr */ |
| 2914 | #line 759 "pikchr.y" |
| 2915 | {yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} |
| 2916 | #line 2941 "pikchr.c" |
| 2917 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2918 | break; |
| 2919 | case 100: /* expr ::= expr STAR expr */ |
| 2920 | #line 760 "pikchr.y" |
| 2921 | {yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} |
| 2922 | #line 2947 "pikchr.c" |
| 2923 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2924 | break; |
| 2925 | case 101: /* expr ::= expr SLASH expr */ |
| 2926 | #line 761 "pikchr.y" |
| 2927 | { |
| 2928 | if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } |
| 2929 | else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } |
| 2930 | } |
| 2931 | #line 2956 "pikchr.c" |
| 2932 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2933 | break; |
| 2934 | case 102: /* expr ::= MINUS expr */ |
| 2935 | #line 765 "pikchr.y" |
| 2936 | {yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} |
| 2937 | #line 2962 "pikchr.c" |
| 2938 | break; |
| 2939 | case 103: /* expr ::= PLUS expr */ |
| 2940 | #line 766 "pikchr.y" |
| 2941 | {yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} |
| 2942 | #line 2967 "pikchr.c" |
| 2943 | break; |
| 2944 | case 104: /* expr ::= LP expr RP */ |
| 2945 | #line 767 "pikchr.y" |
| 2946 | {yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} |
| 2947 | #line 2972 "pikchr.c" |
| 2948 | break; |
| 2949 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2950 | #line 768 "pikchr.y" |
| 2951 | {yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} |
| 2952 | #line 2977 "pikchr.c" |
| 2953 | break; |
| 2954 | case 106: /* expr ::= NUMBER */ |
| 2955 | #line 769 "pikchr.y" |
| 2956 | {yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} |
| 2957 | #line 2982 "pikchr.c" |
| 2958 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2959 | break; |
| 2960 | case 107: /* expr ::= ID */ |
| 2961 | #line 770 "pikchr.y" |
| 2962 | {yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} |
| 2963 | #line 2988 "pikchr.c" |
| 2964 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2965 | break; |
| 2966 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 2967 | #line 771 "pikchr.y" |
| 2968 | {yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} |
| 2969 | #line 2994 "pikchr.c" |
| 2970 | yymsp[-3].minor.yy21 = yylhsminor.yy21; |
| 2971 | break; |
| 2972 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2973 | #line 772 "pikchr.y" |
| 2974 | {yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} |
| 2975 | #line 3000 "pikchr.c" |
| 2976 | yymsp[-5].minor.yy21 = yylhsminor.yy21; |
| 2977 | break; |
| 2978 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 2979 | #line 773 "pikchr.y" |
| 2980 | {yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} |
| 2981 | #line 3006 "pikchr.c" |
| 2982 | break; |
| 2983 | case 111: /* expr ::= place2 DOT_XY X */ |
| 2984 | #line 774 "pikchr.y" |
| 2985 | {yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} |
| 2986 | #line 3011 "pikchr.c" |
| 2987 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2988 | break; |
| 2989 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 2990 | #line 775 "pikchr.y" |
| 2991 | {yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} |
| 2992 | #line 3017 "pikchr.c" |
| 2993 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2994 | break; |
| 2995 | case 113: /* expr ::= object DOT_L numproperty */ |
| 2996 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 2997 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 2998 | #line 776 "pikchr.y" |
| 2999 | {yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 3000 | #line 3025 "pikchr.c" |
| 3001 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 3002 | break; |
| 3003 | default: |
| 3004 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 3005 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3006 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3092,19 +3098,19 @@ | |
| 3098 | ){ |
| 3099 | pik_parserARG_FETCH |
| 3100 | pik_parserCTX_FETCH |
| 3101 | #define TOKEN yyminor |
| 3102 | /************ Begin %syntax_error code ****************************************/ |
| 3103 | #line 536 "pikchr.y" |
| 3104 | |
| 3105 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3106 | pik_error(p, &TOKEN, "syntax error"); |
| 3107 | }else{ |
| 3108 | pik_error(p, 0, "syntax error"); |
| 3109 | } |
| 3110 | UNUSED_PARAMETER(yymajor); |
| 3111 | #line 3136 "pikchr.c" |
| 3112 | /************ End %syntax_error code ******************************************/ |
| 3113 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3114 | pik_parserCTX_STORE |
| 3115 | } |
| 3116 | |
| @@ -3376,11 +3382,11 @@ | |
| 3382 | #else |
| 3383 | (void)iToken; |
| 3384 | return 0; |
| 3385 | #endif |
| 3386 | } |
| 3387 | #line 781 "pikchr.y" |
| 3388 | |
| 3389 | |
| 3390 | |
| 3391 | /* Chart of the 148 official CSS color names with their |
| 3392 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -4978,14 +4984,16 @@ | |
| 4984 | if( t->eCode & TP_RJUST ) nx += jw; |
| 4985 | |
| 4986 | if( pBox!=0 ){ |
| 4987 | /* If pBox is not NULL, do not draw any <text>. Instead, just expand |
| 4988 | ** pBox to include the text */ |
| 4989 | PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01; |
| 4990 | PNum ch = p->charHeight*0.5*xtraFontScale; |
| 4991 | PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */ |
| 4992 | if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){ |
| 4993 | cw *= 1.1; |
| 4994 | } |
| 4995 | if( t->eCode & TP_RJUST ){ |
| 4996 | x0 = nx; |
| 4997 | y0 = y-ch; |
| 4998 | x1 = nx-cw; |
| 4999 | y1 = y+ch; |
| @@ -5037,10 +5045,13 @@ | |
| 5045 | pik_append(p, " font-style=\"italic\"", -1); |
| 5046 | } |
| 5047 | if( t->eCode & TP_BOLD ){ |
| 5048 | pik_append(p, " font-weight=\"bold\"", -1); |
| 5049 | } |
| 5050 | if( t->eCode & TP_MONO ){ |
| 5051 | pik_append(p, " font-family=\"monospace\"", -1); |
| 5052 | } |
| 5053 | if( pObj->color>=0.0 ){ |
| 5054 | pik_append_clr(p, " fill=\"", pObj->color, "\"",0); |
| 5055 | } |
| 5056 | xtraFontScale *= p->fontScale; |
| 5057 | if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){ |
| @@ -6044,12 +6055,13 @@ | |
| 6055 | case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break; |
| 6056 | case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break; |
| 6057 | case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break; |
| 6058 | case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break; |
| 6059 | case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break; |
| 6060 | case T_ITALIC: iRes |= TP_ITALIC; break; |
| 6061 | case T_BOLD: iRes |= TP_BOLD; break; |
| 6062 | case T_MONO: iRes |= TP_MONO; break; |
| 6063 | case T_ALIGNED: iRes |= TP_ALIGN; break; |
| 6064 | case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA; |
| 6065 | else iRes = (iRes &~TP_SZMASK)|TP_BIG; break; |
| 6066 | case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA; |
| 6067 | else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; |
| @@ -6180,15 +6192,17 @@ | |
| 6192 | ** |
| 6193 | ** Omit "\" used to escape characters. And count entities like |
| 6194 | ** "<" as a single character. Multi-byte UTF8 characters count |
| 6195 | ** as a single character. |
| 6196 | ** |
| 6197 | ** Unless using a monospaced font, attempt to scale the answer by |
| 6198 | ** the actual characters seen. Wide characters count more than |
| 6199 | ** narrow characters. But the widths are only guesses. |
| 6200 | ** |
| 6201 | */ |
| 6202 | static int pik_text_length(const PToken *pToken, const int isMonospace){ |
| 6203 | const int stdAvg=100, monoAvg=82; |
| 6204 | int n = pToken->n; |
| 6205 | const char *z = pToken->z; |
| 6206 | int cnt, j; |
| 6207 | for(j=1, cnt=0; j<n-1; j++){ |
| 6208 | char c = z[j]; |
| @@ -6196,22 +6210,24 @@ | |
| 6210 | c = z[++j]; |
| 6211 | }else if( c=='&' ){ |
| 6212 | int k; |
| 6213 | for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} |
| 6214 | if( z[k]==';' ) j = k; |
| 6215 | cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2; |
| 6216 | continue; |
| 6217 | } |
| 6218 | if( (c & 0xc0)==0xc0 ){ |
| 6219 | while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; } |
| 6220 | cnt += isMonospace ? monoAvg : stdAvg; |
| 6221 | continue; |
| 6222 | } |
| 6223 | if( isMonospace ){ |
| 6224 | cnt += monoAvg; |
| 6225 | }else if( c >= 0x20 && c <= 0x7e ){ |
| 6226 | cnt += awChar[c-0x20]; |
| 6227 | }else{ |
| 6228 | cnt += stdAvg; |
| 6229 | } |
| 6230 | } |
| 6231 | return cnt; |
| 6232 | } |
| 6233 | |
| @@ -7195,10 +7211,12 @@ | |
| 7211 | { "last", 4, T_LAST, 0, 0 }, |
| 7212 | { "left", 4, T_LEFT, DIR_LEFT, CP_W }, |
| 7213 | { "ljust", 5, T_LJUST, 0, 0 }, |
| 7214 | { "max", 3, T_FUNC2, FN_MAX, 0 }, |
| 7215 | { "min", 3, T_FUNC2, FN_MIN, 0 }, |
| 7216 | { "mono", 4, T_MONO, 0, 0 }, |
| 7217 | { "monospace", 9, T_MONO, 0, 0 }, |
| 7218 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7219 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7220 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7221 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7222 | { "of", 2, T_OF, 0, 0 }, |
| @@ -8122,6 +8140,6 @@ | |
| 8140 | |
| 8141 | |
| 8142 | #endif /* PIKCHR_TCL */ |
| 8143 | |
| 8144 | |
| 8145 | #line 8170 "pikchr.c" |
| 8146 |
+17
-15
| --- extsrc/pikchr.js | ||
| +++ extsrc/pikchr.js | ||
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | |
| 2 | 2 | var initPikchrModule = (() => { |
| 3 | 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; |
| 4 | 4 | |
| 5 | 5 | return ( |
| 6 | -function(initPikchrModule) { | |
| 7 | - initPikchrModule = initPikchrModule || {}; | |
| 6 | +function(config) { | |
| 7 | + var initPikchrModule = config || {}; | |
| 8 | 8 | |
| 9 | 9 | var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {}; |
| 10 | 10 | |
| 11 | 11 | var readyPromiseResolve, readyPromiseReject; |
| 12 | 12 | |
| @@ -195,22 +195,22 @@ | ||
| 195 | 195 | |
| 196 | 196 | function stringToUTF8(str, outPtr, maxBytesToWrite) { |
| 197 | 197 | return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | -var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; | |
| 201 | - | |
| 202 | -function updateGlobalBufferAndViews(buf) { | |
| 203 | - buffer = buf; | |
| 204 | - Module["HEAP8"] = HEAP8 = new Int8Array(buf); | |
| 205 | - Module["HEAP16"] = HEAP16 = new Int16Array(buf); | |
| 206 | - Module["HEAP32"] = HEAP32 = new Int32Array(buf); | |
| 207 | - Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); | |
| 208 | - Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); | |
| 209 | - Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); | |
| 210 | - Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); | |
| 211 | - Module["HEAPF64"] = HEAPF64 = new Float64Array(buf); | |
| 200 | +var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; | |
| 201 | + | |
| 202 | +function updateMemoryViews() { | |
| 203 | + var b = wasmMemory.buffer; | |
| 204 | + Module["HEAP8"] = HEAP8 = new Int8Array(b); | |
| 205 | + Module["HEAP16"] = HEAP16 = new Int16Array(b); | |
| 206 | + Module["HEAP32"] = HEAP32 = new Int32Array(b); | |
| 207 | + Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); | |
| 208 | + Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); | |
| 209 | + Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); | |
| 210 | + Module["HEAPF32"] = HEAPF32 = new Float32Array(b); | |
| 211 | + Module["HEAPF64"] = HEAPF64 = new Float64Array(b); | |
| 212 | 212 | } |
| 213 | 213 | |
| 214 | 214 | var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; |
| 215 | 215 | |
| 216 | 216 | var wasmTable; |
| @@ -363,11 +363,11 @@ | ||
| 363 | 363 | }; |
| 364 | 364 | function receiveInstance(instance, module) { |
| 365 | 365 | var exports = instance.exports; |
| 366 | 366 | Module["asm"] = exports; |
| 367 | 367 | wasmMemory = Module["asm"]["d"]; |
| 368 | - updateGlobalBufferAndViews(wasmMemory.buffer); | |
| 368 | + updateMemoryViews(); | |
| 369 | 369 | wasmTable = Module["asm"]["g"]; |
| 370 | 370 | addOnInit(Module["asm"]["e"]); |
| 371 | 371 | removeRunDependency("wasm-instantiate"); |
| 372 | 372 | } |
| 373 | 373 | addRunDependency("wasm-instantiate"); |
| @@ -639,10 +639,12 @@ | ||
| 639 | 639 | }; |
| 640 | 640 | |
| 641 | 641 | var stackAlloc = Module["stackAlloc"] = function() { |
| 642 | 642 | return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments); |
| 643 | 643 | }; |
| 644 | + | |
| 645 | +Module["stackAlloc"] = stackAlloc; | |
| 644 | 646 | |
| 645 | 647 | Module["stackSave"] = stackSave; |
| 646 | 648 | |
| 647 | 649 | Module["stackRestore"] = stackRestore; |
| 648 | 650 | |
| 649 | 651 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -1,12 +1,12 @@ | |
| 1 | |
| 2 | var initPikchrModule = (() => { |
| 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; |
| 4 | |
| 5 | return ( |
| 6 | function(initPikchrModule) { |
| 7 | initPikchrModule = initPikchrModule || {}; |
| 8 | |
| 9 | var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {}; |
| 10 | |
| 11 | var readyPromiseResolve, readyPromiseReject; |
| 12 | |
| @@ -195,22 +195,22 @@ | |
| 195 | |
| 196 | function stringToUTF8(str, outPtr, maxBytesToWrite) { |
| 197 | return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); |
| 198 | } |
| 199 | |
| 200 | var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; |
| 201 | |
| 202 | function updateGlobalBufferAndViews(buf) { |
| 203 | buffer = buf; |
| 204 | Module["HEAP8"] = HEAP8 = new Int8Array(buf); |
| 205 | Module["HEAP16"] = HEAP16 = new Int16Array(buf); |
| 206 | Module["HEAP32"] = HEAP32 = new Int32Array(buf); |
| 207 | Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); |
| 208 | Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); |
| 209 | Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); |
| 210 | Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); |
| 211 | Module["HEAPF64"] = HEAPF64 = new Float64Array(buf); |
| 212 | } |
| 213 | |
| 214 | var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; |
| 215 | |
| 216 | var wasmTable; |
| @@ -363,11 +363,11 @@ | |
| 363 | }; |
| 364 | function receiveInstance(instance, module) { |
| 365 | var exports = instance.exports; |
| 366 | Module["asm"] = exports; |
| 367 | wasmMemory = Module["asm"]["d"]; |
| 368 | updateGlobalBufferAndViews(wasmMemory.buffer); |
| 369 | wasmTable = Module["asm"]["g"]; |
| 370 | addOnInit(Module["asm"]["e"]); |
| 371 | removeRunDependency("wasm-instantiate"); |
| 372 | } |
| 373 | addRunDependency("wasm-instantiate"); |
| @@ -639,10 +639,12 @@ | |
| 639 | }; |
| 640 | |
| 641 | var stackAlloc = Module["stackAlloc"] = function() { |
| 642 | return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments); |
| 643 | }; |
| 644 | |
| 645 | Module["stackSave"] = stackSave; |
| 646 | |
| 647 | Module["stackRestore"] = stackRestore; |
| 648 | |
| 649 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -1,12 +1,12 @@ | |
| 1 | |
| 2 | var initPikchrModule = (() => { |
| 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; |
| 4 | |
| 5 | return ( |
| 6 | function(config) { |
| 7 | var initPikchrModule = config || {}; |
| 8 | |
| 9 | var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {}; |
| 10 | |
| 11 | var readyPromiseResolve, readyPromiseReject; |
| 12 | |
| @@ -195,22 +195,22 @@ | |
| 195 | |
| 196 | function stringToUTF8(str, outPtr, maxBytesToWrite) { |
| 197 | return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); |
| 198 | } |
| 199 | |
| 200 | var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; |
| 201 | |
| 202 | function updateMemoryViews() { |
| 203 | var b = wasmMemory.buffer; |
| 204 | Module["HEAP8"] = HEAP8 = new Int8Array(b); |
| 205 | Module["HEAP16"] = HEAP16 = new Int16Array(b); |
| 206 | Module["HEAP32"] = HEAP32 = new Int32Array(b); |
| 207 | Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); |
| 208 | Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); |
| 209 | Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); |
| 210 | Module["HEAPF32"] = HEAPF32 = new Float32Array(b); |
| 211 | Module["HEAPF64"] = HEAPF64 = new Float64Array(b); |
| 212 | } |
| 213 | |
| 214 | var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; |
| 215 | |
| 216 | var wasmTable; |
| @@ -363,11 +363,11 @@ | |
| 363 | }; |
| 364 | function receiveInstance(instance, module) { |
| 365 | var exports = instance.exports; |
| 366 | Module["asm"] = exports; |
| 367 | wasmMemory = Module["asm"]["d"]; |
| 368 | updateMemoryViews(); |
| 369 | wasmTable = Module["asm"]["g"]; |
| 370 | addOnInit(Module["asm"]["e"]); |
| 371 | removeRunDependency("wasm-instantiate"); |
| 372 | } |
| 373 | addRunDependency("wasm-instantiate"); |
| @@ -639,10 +639,12 @@ | |
| 639 | }; |
| 640 | |
| 641 | var stackAlloc = Module["stackAlloc"] = function() { |
| 642 | return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments); |
| 643 | }; |
| 644 | |
| 645 | Module["stackAlloc"] = stackAlloc; |
| 646 | |
| 647 | Module["stackSave"] = stackSave; |
| 648 | |
| 649 | Module["stackRestore"] = stackRestore; |
| 650 | |
| 651 |
| --- extsrc/pikchr.wasm | ||
| +++ extsrc/pikchr.wasm | ||
| cannot compute difference between binary files | ||
| 1 | 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
+594
-151
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -244,10 +244,11 @@ | ||
| 244 | 244 | |
| 245 | 245 | #if defined(_WIN32) || defined(WIN32) |
| 246 | 246 | #if SQLITE_OS_WINRT |
| 247 | 247 | #include <intrin.h> |
| 248 | 248 | #endif |
| 249 | +#define WIN32_LEAN_AND_MEAN | |
| 249 | 250 | #include <windows.h> |
| 250 | 251 | |
| 251 | 252 | /* string conversion routines only needed on Win32 */ |
| 252 | 253 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 253 | 254 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -458,14 +459,28 @@ | ||
| 458 | 459 | ** at an error if we are not interactive. |
| 459 | 460 | */ |
| 460 | 461 | static int bail_on_error = 0; |
| 461 | 462 | |
| 462 | 463 | /* |
| 463 | -** Threat stdin as an interactive input if the following variable | |
| 464 | +** Treat stdin as an interactive input if the following variable | |
| 464 | 465 | ** is true. Otherwise, assume stdin is connected to a file or pipe. |
| 465 | 466 | */ |
| 466 | 467 | static int stdin_is_interactive = 1; |
| 468 | + | |
| 469 | +#if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \ | |
| 470 | + && !defined(SHELL_OMIT_WIN_UTF8) | |
| 471 | +# define SHELL_WIN_UTF8_OPT 1 | |
| 472 | +#else | |
| 473 | +# define SHELL_WIN_UTF8_OPT 0 | |
| 474 | +#endif | |
| 475 | + | |
| 476 | +#if SHELL_WIN_UTF8_OPT | |
| 477 | +/* | |
| 478 | +** Setup console for UTF-8 input/output when following variable true. | |
| 479 | +*/ | |
| 480 | +static int console_utf8 = 0; | |
| 481 | +#endif | |
| 467 | 482 | |
| 468 | 483 | /* |
| 469 | 484 | ** On Windows systems we have to know if standard output is a console |
| 470 | 485 | ** in order to translate UTF-8 into MBCS. The following variable is |
| 471 | 486 | ** true if translation is required. |
| @@ -595,20 +610,154 @@ | ||
| 595 | 610 | } |
| 596 | 611 | return dynPrompt.dynamicPrompt; |
| 597 | 612 | } |
| 598 | 613 | #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ |
| 599 | 614 | |
| 615 | +#if SHELL_WIN_UTF8_OPT | |
| 616 | +/* Following struct is used for -utf8 operation. */ | |
| 617 | +static struct ConsoleState { | |
| 618 | + int stdinEof; /* EOF has been seen on console input */ | |
| 619 | + int infsMode; /* Input file stream mode upon shell start */ | |
| 620 | + UINT inCodePage; /* Input code page upon shell start */ | |
| 621 | + UINT outCodePage; /* Output code page upon shell start */ | |
| 622 | + HANDLE hConsoleIn; /* Console input handle */ | |
| 623 | + DWORD consoleMode; /* Console mode upon shell start */ | |
| 624 | +} conState = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 }; | |
| 625 | + | |
| 626 | +#ifndef _O_U16TEXT /* For build environments lacking this constant: */ | |
| 627 | +# define _O_U16TEXT 0x20000 | |
| 628 | +#endif | |
| 629 | + | |
| 630 | +/* | |
| 631 | +** Prepare console, (if known to be a WIN32 console), for UTF-8 | |
| 632 | +** input (from either typing or suitable paste operations) and for | |
| 633 | +** UTF-8 rendering. This may "fail" with a message to stderr, where | |
| 634 | +** the preparation is not done and common "code page" issues occur. | |
| 635 | +*/ | |
| 636 | +static void console_prepare(void){ | |
| 637 | + HANDLE hCI = GetStdHandle(STD_INPUT_HANDLE); | |
| 638 | + DWORD consoleMode = 0; | |
| 639 | + if( isatty(0) && GetFileType(hCI)==FILE_TYPE_CHAR | |
| 640 | + && GetConsoleMode( hCI, &consoleMode) ){ | |
| 641 | + if( !IsValidCodePage(CP_UTF8) ){ | |
| 642 | + fprintf(stderr, "Cannot use UTF-8 code page.\n"); | |
| 643 | + console_utf8 = 0; | |
| 644 | + return; | |
| 645 | + } | |
| 646 | + conState.hConsoleIn = hCI; | |
| 647 | + conState.consoleMode = consoleMode; | |
| 648 | + conState.inCodePage = GetConsoleCP(); | |
| 649 | + conState.outCodePage = GetConsoleOutputCP(); | |
| 650 | + SetConsoleCP(CP_UTF8); | |
| 651 | + SetConsoleOutputCP(CP_UTF8); | |
| 652 | + consoleMode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; | |
| 653 | + SetConsoleMode(conState.hConsoleIn, consoleMode); | |
| 654 | + conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT); | |
| 655 | + console_utf8 = 1; | |
| 656 | + }else{ | |
| 657 | + console_utf8 = 0; | |
| 658 | + } | |
| 659 | +} | |
| 660 | + | |
| 661 | +/* | |
| 662 | +** Undo the effects of console_prepare(), if any. | |
| 663 | +*/ | |
| 664 | +static void SQLITE_CDECL console_restore(void){ | |
| 665 | + if( console_utf8 && conState.inCodePage!=0 | |
| 666 | + && conState.hConsoleIn!=INVALID_HANDLE_VALUE ){ | |
| 667 | + _setmode(_fileno(stdin), conState.infsMode); | |
| 668 | + SetConsoleCP(conState.inCodePage); | |
| 669 | + SetConsoleOutputCP(conState.outCodePage); | |
| 670 | + SetConsoleMode(conState.hConsoleIn, conState.consoleMode); | |
| 671 | + /* Avoid multiple calls. */ | |
| 672 | + conState.hConsoleIn = INVALID_HANDLE_VALUE; | |
| 673 | + conState.consoleMode = 0; | |
| 674 | + console_utf8 = 0; | |
| 675 | + } | |
| 676 | +} | |
| 677 | + | |
| 678 | +/* | |
| 679 | +** Collect input like fgets(...) with special provisions for input | |
| 680 | +** from the Windows console to get around its strange coding issues. | |
| 681 | +** Defers to plain fgets() when input is not interactive or when the | |
| 682 | +** startup option, -utf8, has not been provided or taken effect. | |
| 683 | +*/ | |
| 684 | +static char* utf8_fgets(char *buf, int ncmax, FILE *fin){ | |
| 685 | + if( fin==0 ) fin = stdin; | |
| 686 | + if( fin==stdin && stdin_is_interactive && console_utf8 ){ | |
| 687 | +# define SQLITE_IALIM 150 | |
| 688 | + wchar_t wbuf[SQLITE_IALIM]; | |
| 689 | + int lend = 0; | |
| 690 | + int noc = 0; | |
| 691 | + if( ncmax==0 || conState.stdinEof ) return 0; | |
| 692 | + buf[0] = 0; | |
| 693 | + while( noc<ncmax-7-1 && !lend ){ | |
| 694 | + /* There is room for at least 2 more characters and a 0-terminator. */ | |
| 695 | + int na = (ncmax > SQLITE_IALIM*4+1 + noc) | |
| 696 | + ? SQLITE_IALIM : (ncmax-1 - noc)/4; | |
| 697 | +# undef SQLITE_IALIM | |
| 698 | + DWORD nbr = 0; | |
| 699 | + BOOL bRC = ReadConsoleW(conState.hConsoleIn, wbuf, na, &nbr, 0); | |
| 700 | + if( !bRC || (noc==0 && nbr==0) ) return 0; | |
| 701 | + if( nbr > 0 ){ | |
| 702 | + int nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, | |
| 703 | + wbuf,nbr,0,0,0,0); | |
| 704 | + if( nmb !=0 && noc+nmb <= ncmax ){ | |
| 705 | + int iseg = noc; | |
| 706 | + nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, | |
| 707 | + wbuf,nbr,buf+noc,nmb,0,0); | |
| 708 | + noc += nmb; | |
| 709 | + /* Fixup line-ends as coded by Windows for CR (or "Enter".)*/ | |
| 710 | + if( noc > 0 ){ | |
| 711 | + if( buf[noc-1]=='\n' ){ | |
| 712 | + lend = 1; | |
| 713 | + if( noc > 1 && buf[noc-2]=='\r' ){ | |
| 714 | + buf[noc-2] = '\n'; | |
| 715 | + --noc; | |
| 716 | + } | |
| 717 | + } | |
| 718 | + } | |
| 719 | + /* Check for ^Z (anywhere in line) too. */ | |
| 720 | + while( iseg < noc ){ | |
| 721 | + if( buf[iseg]==0x1a ){ | |
| 722 | + conState.stdinEof = 1; | |
| 723 | + noc = iseg; /* Chop ^Z and anything following. */ | |
| 724 | + break; | |
| 725 | + } | |
| 726 | + ++iseg; | |
| 727 | + } | |
| 728 | + }else break; /* Drop apparent garbage in. (Could assert.) */ | |
| 729 | + }else break; | |
| 730 | + } | |
| 731 | + /* If got nothing, (after ^Z chop), must be at end-of-file. */ | |
| 732 | + if( noc == 0 ) return 0; | |
| 733 | + buf[noc] = 0; | |
| 734 | + return buf; | |
| 735 | + }else{ | |
| 736 | + return fgets(buf, ncmax, fin); | |
| 737 | + } | |
| 738 | +} | |
| 739 | + | |
| 740 | +# define fgets(b,n,f) utf8_fgets(b,n,f) | |
| 741 | +#endif /* SHELL_WIN_UTF8_OPT */ | |
| 742 | + | |
| 600 | 743 | /* |
| 601 | 744 | ** Render output like fprintf(). Except, if the output is going to the |
| 602 | -** console and if this is running on a Windows machine, translate the | |
| 603 | -** output from UTF-8 into MBCS. | |
| 745 | +** console and if this is running on a Windows machine, and if the -utf8 | |
| 746 | +** option is unavailable or (available and inactive), translate the | |
| 747 | +** output from UTF-8 into MBCS for output through 8-bit stdout stream. | |
| 748 | +** (With -utf8 active, no translation is needed and must not be done.) | |
| 604 | 749 | */ |
| 605 | 750 | #if defined(_WIN32) || defined(WIN32) |
| 606 | 751 | void utf8_printf(FILE *out, const char *zFormat, ...){ |
| 607 | 752 | va_list ap; |
| 608 | 753 | va_start(ap, zFormat); |
| 609 | - if( stdout_is_console && (out==stdout || out==stderr) ){ | |
| 754 | + if( stdout_is_console && (out==stdout || out==stderr) | |
| 755 | +# if SHELL_WIN_UTF8_OPT | |
| 756 | + && !console_utf8 | |
| 757 | +# endif | |
| 758 | + ){ | |
| 610 | 759 | char *z1 = sqlite3_vmprintf(zFormat, ap); |
| 611 | 760 | char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); |
| 612 | 761 | sqlite3_free(z1); |
| 613 | 762 | fputs(z2, out); |
| 614 | 763 | sqlite3_free(z2); |
| @@ -636,11 +785,11 @@ | ||
| 636 | 785 | } |
| 637 | 786 | |
| 638 | 787 | /* Check a pointer to see if it is NULL. If it is NULL, exit with an |
| 639 | 788 | ** out-of-memory error. |
| 640 | 789 | */ |
| 641 | -static void shell_check_oom(void *p){ | |
| 790 | +static void shell_check_oom(const void *p){ | |
| 642 | 791 | if( p==0 ) shell_out_of_memory(); |
| 643 | 792 | } |
| 644 | 793 | |
| 645 | 794 | /* |
| 646 | 795 | ** Write I/O traces to the following stream. |
| @@ -815,13 +964,18 @@ | ||
| 815 | 964 | zLine[n] = 0; |
| 816 | 965 | break; |
| 817 | 966 | } |
| 818 | 967 | } |
| 819 | 968 | #if defined(_WIN32) || defined(WIN32) |
| 820 | - /* For interactive input on Windows systems, translate the | |
| 821 | - ** multi-byte characterset characters into UTF-8. */ | |
| 822 | - if( stdin_is_interactive && in==stdin ){ | |
| 969 | + /* For interactive input on Windows systems, without -utf8, | |
| 970 | + ** translate the multi-byte characterset characters into UTF-8. | |
| 971 | + ** This is the translation that predates the -utf8 option. */ | |
| 972 | + if( stdin_is_interactive && in==stdin | |
| 973 | +# if SHELL_WIN_UTF8_OPT | |
| 974 | + && !console_utf8 | |
| 975 | +# endif /* SHELL_WIN_UTF8_OPT */ | |
| 976 | + ){ | |
| 823 | 977 | char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); |
| 824 | 978 | if( zTrans ){ |
| 825 | 979 | i64 nTrans = strlen(zTrans)+1; |
| 826 | 980 | if( nTrans>nLine ){ |
| 827 | 981 | zLine = realloc(zLine, nTrans); |
| @@ -858,14 +1012,25 @@ | ||
| 858 | 1012 | }else{ |
| 859 | 1013 | zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt; |
| 860 | 1014 | #if SHELL_USE_LOCAL_GETLINE |
| 861 | 1015 | printf("%s", zPrompt); |
| 862 | 1016 | fflush(stdout); |
| 863 | - zResult = local_getline(zPrior, stdin); | |
| 1017 | + do{ | |
| 1018 | + zResult = local_getline(zPrior, stdin); | |
| 1019 | + zPrior = 0; | |
| 1020 | + /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ | |
| 1021 | + if( zResult==0 ) sqlite3_sleep(50); | |
| 1022 | + }while( zResult==0 && seenInterrupt>0 ); | |
| 864 | 1023 | #else |
| 865 | 1024 | free(zPrior); |
| 866 | 1025 | zResult = shell_readline(zPrompt); |
| 1026 | + while( zResult==0 ){ | |
| 1027 | + /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ | |
| 1028 | + sqlite3_sleep(50); | |
| 1029 | + if( seenInterrupt==0 ) break; | |
| 1030 | + zResult = shell_readline(""); | |
| 1031 | + } | |
| 867 | 1032 | if( zResult && *zResult ) shell_add_history(zResult); |
| 868 | 1033 | #endif |
| 869 | 1034 | } |
| 870 | 1035 | return zResult; |
| 871 | 1036 | } |
| @@ -1001,10 +1166,11 @@ | ||
| 1001 | 1166 | ** |
| 1002 | 1167 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1003 | 1168 | */ |
| 1004 | 1169 | static char quoteChar(const char *zName){ |
| 1005 | 1170 | int i; |
| 1171 | + if( zName==0 ) return '"'; | |
| 1006 | 1172 | if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; |
| 1007 | 1173 | for(i=0; zName[i]; i++){ |
| 1008 | 1174 | if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; |
| 1009 | 1175 | } |
| 1010 | 1176 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| @@ -1644,11 +1810,12 @@ | ||
| 1644 | 1810 | ** May you find forgiveness for yourself and forgive others. |
| 1645 | 1811 | ** May you share freely, never taking more than you give. |
| 1646 | 1812 | ** |
| 1647 | 1813 | ****************************************************************************** |
| 1648 | 1814 | ** |
| 1649 | -** This SQLite extension implements functions that compute SHA3 hashes. | |
| 1815 | +** This SQLite extension implements functions that compute SHA3 hashes | |
| 1816 | +** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. | |
| 1650 | 1817 | ** Two SQL functions are implemented: |
| 1651 | 1818 | ** |
| 1652 | 1819 | ** sha3(X,SIZE) |
| 1653 | 1820 | ** sha3_query(Y,SIZE) |
| 1654 | 1821 | ** |
| @@ -3244,22 +3411,22 @@ | ||
| 3244 | 3411 | *pOut = 0; |
| 3245 | 3412 | return pOut; |
| 3246 | 3413 | } |
| 3247 | 3414 | |
| 3248 | 3415 | /* Skip over text which is not base64 numeral(s). */ |
| 3249 | -static char * skipNonB64( char *s ){ | |
| 3416 | +static char * skipNonB64( char *s, int nc ){ | |
| 3250 | 3417 | char c; |
| 3251 | - while( (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s; | |
| 3418 | + while( nc-- > 0 && (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s; | |
| 3252 | 3419 | return s; |
| 3253 | 3420 | } |
| 3254 | 3421 | |
| 3255 | 3422 | /* Decode base64 text into a byte buffer. */ |
| 3256 | 3423 | static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){ |
| 3257 | 3424 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3258 | 3425 | while( ncIn>0 && *pIn!=PAD_CHAR ){ |
| 3259 | 3426 | static signed char nboi[] = { 0, 0, 1, 2, 3 }; |
| 3260 | - char *pUse = skipNonB64(pIn); | |
| 3427 | + char *pUse = skipNonB64(pIn, ncIn); | |
| 3261 | 3428 | unsigned long qv = 0L; |
| 3262 | 3429 | int nti, nbo, nac; |
| 3263 | 3430 | ncIn -= (pUse - pIn); |
| 3264 | 3431 | pIn = pUse; |
| 3265 | 3432 | nti = (ncIn>4)? 4 : ncIn; |
| @@ -3314,14 +3481,21 @@ | ||
| 3314 | 3481 | nc = 4*(nv+2/3); /* quads needed */ |
| 3315 | 3482 | nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */ |
| 3316 | 3483 | if( nvMax < nc ){ |
| 3317 | 3484 | sqlite3_result_error(context, "blob expanded to base64 too big", -1); |
| 3318 | 3485 | return; |
| 3486 | + } | |
| 3487 | + bBuf = (u8*)sqlite3_value_blob(av[0]); | |
| 3488 | + if( !bBuf ){ | |
| 3489 | + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ | |
| 3490 | + goto memFail; | |
| 3491 | + } | |
| 3492 | + sqlite3_result_text(context,"",-1,SQLITE_STATIC); | |
| 3493 | + break; | |
| 3319 | 3494 | } |
| 3320 | 3495 | cBuf = sqlite3_malloc(nc); |
| 3321 | 3496 | if( !cBuf ) goto memFail; |
| 3322 | - bBuf = (u8*)sqlite3_value_blob(av[0]); | |
| 3323 | 3497 | nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf); |
| 3324 | 3498 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3325 | 3499 | break; |
| 3326 | 3500 | case SQLITE_TEXT: |
| 3327 | 3501 | nc = nv; |
| @@ -3329,14 +3503,21 @@ | ||
| 3329 | 3503 | if( nvMax < nb ){ |
| 3330 | 3504 | sqlite3_result_error(context, "blob from base64 may be too big", -1); |
| 3331 | 3505 | return; |
| 3332 | 3506 | }else if( nb<1 ){ |
| 3333 | 3507 | nb = 1; |
| 3508 | + } | |
| 3509 | + cBuf = (char *)sqlite3_value_text(av[0]); | |
| 3510 | + if( !cBuf ){ | |
| 3511 | + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ | |
| 3512 | + goto memFail; | |
| 3513 | + } | |
| 3514 | + sqlite3_result_zeroblob(context, 0); | |
| 3515 | + break; | |
| 3334 | 3516 | } |
| 3335 | 3517 | bBuf = sqlite3_malloc(nb); |
| 3336 | 3518 | if( !bBuf ) goto memFail; |
| 3337 | - cBuf = (char *)sqlite3_value_text(av[0]); | |
| 3338 | 3519 | nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf); |
| 3339 | 3520 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3340 | 3521 | break; |
| 3341 | 3522 | default: |
| 3342 | 3523 | sqlite3_result_error(context, "base64 accepts only blob or text", -1); |
| @@ -3520,13 +3701,13 @@ | ||
| 3520 | 3701 | |
| 3521 | 3702 | /* Width of base64 lines. Should be an integer multiple of 5. */ |
| 3522 | 3703 | #define B85_DARK_MAX 80 |
| 3523 | 3704 | |
| 3524 | 3705 | |
| 3525 | -static char * skipNonB85( char *s ){ | |
| 3706 | +static char * skipNonB85( char *s, int nc ){ | |
| 3526 | 3707 | char c; |
| 3527 | - while( (c = *s) && !IS_B85(c) ) ++s; | |
| 3708 | + while( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s; | |
| 3528 | 3709 | return s; |
| 3529 | 3710 | } |
| 3530 | 3711 | |
| 3531 | 3712 | /* Convert small integer, known to be in 0..84 inclusive, to base85 numeral. |
| 3532 | 3713 | * Do not use the macro form with argument expression having a side-effect.*/ |
| @@ -3592,11 +3773,11 @@ | ||
| 3592 | 3773 | /* Decode base85 text into a byte buffer. */ |
| 3593 | 3774 | static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){ |
| 3594 | 3775 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3595 | 3776 | while( ncIn>0 ){ |
| 3596 | 3777 | static signed char nboi[] = { 0, 0, 1, 2, 3, 4 }; |
| 3597 | - char *pUse = skipNonB85(pIn); | |
| 3778 | + char *pUse = skipNonB85(pIn, ncIn); | |
| 3598 | 3779 | unsigned long qv = 0L; |
| 3599 | 3780 | int nti, nbo; |
| 3600 | 3781 | ncIn -= (pUse - pIn); |
| 3601 | 3782 | pIn = pUse; |
| 3602 | 3783 | nti = (ncIn>5)? 5 : ncIn; |
| @@ -3676,14 +3857,21 @@ | ||
| 3676 | 3857 | /* ulongs tail newlines tailenc+nul*/ |
| 3677 | 3858 | nc = 5*(nv/4) + nv%4 + nv/64+1 + 2; |
| 3678 | 3859 | if( nvMax < nc ){ |
| 3679 | 3860 | sqlite3_result_error(context, "blob expanded to base85 too big", -1); |
| 3680 | 3861 | return; |
| 3862 | + } | |
| 3863 | + bBuf = (u8*)sqlite3_value_blob(av[0]); | |
| 3864 | + if( !bBuf ){ | |
| 3865 | + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ | |
| 3866 | + goto memFail; | |
| 3867 | + } | |
| 3868 | + sqlite3_result_text(context,"",-1,SQLITE_STATIC); | |
| 3869 | + break; | |
| 3681 | 3870 | } |
| 3682 | 3871 | cBuf = sqlite3_malloc(nc); |
| 3683 | 3872 | if( !cBuf ) goto memFail; |
| 3684 | - bBuf = (u8*)sqlite3_value_blob(av[0]); | |
| 3685 | 3873 | nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf); |
| 3686 | 3874 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3687 | 3875 | break; |
| 3688 | 3876 | case SQLITE_TEXT: |
| 3689 | 3877 | nc = nv; |
| @@ -3691,14 +3879,21 @@ | ||
| 3691 | 3879 | if( nvMax < nb ){ |
| 3692 | 3880 | sqlite3_result_error(context, "blob from base85 may be too big", -1); |
| 3693 | 3881 | return; |
| 3694 | 3882 | }else if( nb<1 ){ |
| 3695 | 3883 | nb = 1; |
| 3884 | + } | |
| 3885 | + cBuf = (char *)sqlite3_value_text(av[0]); | |
| 3886 | + if( !cBuf ){ | |
| 3887 | + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ | |
| 3888 | + goto memFail; | |
| 3889 | + } | |
| 3890 | + sqlite3_result_zeroblob(context, 0); | |
| 3891 | + break; | |
| 3696 | 3892 | } |
| 3697 | 3893 | bBuf = sqlite3_malloc(nb); |
| 3698 | 3894 | if( !bBuf ) goto memFail; |
| 3699 | - cBuf = (char *)sqlite3_value_text(av[0]); | |
| 3700 | 3895 | nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf); |
| 3701 | 3896 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3702 | 3897 | break; |
| 3703 | 3898 | default: |
| 3704 | 3899 | sqlite3_result_error(context, "base85 accepts only blob or text.", -1); |
| @@ -4114,11 +4309,11 @@ | ||
| 4114 | 4309 | } |
| 4115 | 4310 | |
| 4116 | 4311 | /************************* End ../ext/misc/ieee754.c ********************/ |
| 4117 | 4312 | /************************* Begin ../ext/misc/series.c ******************/ |
| 4118 | 4313 | /* |
| 4119 | -** 2015-08-18 | |
| 4314 | +** 2015-08-18, 2023-04-28 | |
| 4120 | 4315 | ** |
| 4121 | 4316 | ** The author disclaims copyright to this source code. In place of |
| 4122 | 4317 | ** a legal notice, here is a blessing: |
| 4123 | 4318 | ** |
| 4124 | 4319 | ** May you do good and not evil. |
| @@ -4127,11 +4322,23 @@ | ||
| 4127 | 4322 | ** |
| 4128 | 4323 | ************************************************************************* |
| 4129 | 4324 | ** |
| 4130 | 4325 | ** This file demonstrates how to create a table-valued-function using |
| 4131 | 4326 | ** a virtual table. This demo implements the generate_series() function |
| 4132 | -** which gives similar results to the eponymous function in PostgreSQL. | |
| 4327 | +** which gives the same results as the eponymous function in PostgreSQL, | |
| 4328 | +** within the limitation that its arguments are signed 64-bit integers. | |
| 4329 | +** | |
| 4330 | +** Considering its equivalents to generate_series(start,stop,step): A | |
| 4331 | +** value V[n] sequence is produced for integer n ascending from 0 where | |
| 4332 | +** ( V[n] == start + n * step && sgn(V[n] - stop) * sgn(step) >= 0 ) | |
| 4333 | +** for each produced value (independent of production time ordering.) | |
| 4334 | +** | |
| 4335 | +** All parameters must be either integer or convertable to integer. | |
| 4336 | +** The start parameter is required. | |
| 4337 | +** The stop parameter defaults to (1<<32)-1 (aka 4294967295 or 0xffffffff) | |
| 4338 | +** The step parameter defaults to 1 and 0 is treated as 1. | |
| 4339 | +** | |
| 4133 | 4340 | ** Examples: |
| 4134 | 4341 | ** |
| 4135 | 4342 | ** SELECT * FROM generate_series(0,100,5); |
| 4136 | 4343 | ** |
| 4137 | 4344 | ** The query above returns integers from 0 through 100 counting by steps |
| @@ -4143,10 +4350,18 @@ | ||
| 4143 | 4350 | ** |
| 4144 | 4351 | ** SELECT * FROM generate_series(20) LIMIT 10; |
| 4145 | 4352 | ** |
| 4146 | 4353 | ** Integers 20 through 29. |
| 4147 | 4354 | ** |
| 4355 | +** SELECT * FROM generate_series(0,-100,-5); | |
| 4356 | +** | |
| 4357 | +** Integers 0 -5 -10 ... -100. | |
| 4358 | +** | |
| 4359 | +** SELECT * FROM generate_series(0,-1); | |
| 4360 | +** | |
| 4361 | +** Empty sequence. | |
| 4362 | +** | |
| 4148 | 4363 | ** HOW IT WORKS |
| 4149 | 4364 | ** |
| 4150 | 4365 | ** The generate_series "function" is really a virtual table with the |
| 4151 | 4366 | ** following schema: |
| 4152 | 4367 | ** |
| @@ -4154,10 +4369,13 @@ | ||
| 4154 | 4369 | ** value, |
| 4155 | 4370 | ** start HIDDEN, |
| 4156 | 4371 | ** stop HIDDEN, |
| 4157 | 4372 | ** step HIDDEN |
| 4158 | 4373 | ** ); |
| 4374 | +** | |
| 4375 | +** The virtual table also has a rowid, logically equivalent to n+1 where | |
| 4376 | +** "n" is the ascending integer in the aforesaid production definition. | |
| 4159 | 4377 | ** |
| 4160 | 4378 | ** Function arguments in queries against this virtual table are translated |
| 4161 | 4379 | ** into equality constraints against successive hidden columns. In other |
| 4162 | 4380 | ** words, the following pairs of queries are equivalent to each other: |
| 4163 | 4381 | ** |
| @@ -4187,27 +4405,139 @@ | ||
| 4187 | 4405 | */ |
| 4188 | 4406 | /* #include "sqlite3ext.h" */ |
| 4189 | 4407 | SQLITE_EXTENSION_INIT1 |
| 4190 | 4408 | #include <assert.h> |
| 4191 | 4409 | #include <string.h> |
| 4410 | +#include <limits.h> | |
| 4192 | 4411 | |
| 4193 | 4412 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 4413 | +/* | |
| 4414 | +** Return that member of a generate_series(...) sequence whose 0-based | |
| 4415 | +** index is ix. The 0th member is given by smBase. The sequence members | |
| 4416 | +** progress per ix increment by smStep. | |
| 4417 | +*/ | |
| 4418 | +static sqlite3_int64 genSeqMember(sqlite3_int64 smBase, | |
| 4419 | + sqlite3_int64 smStep, | |
| 4420 | + sqlite3_uint64 ix){ | |
| 4421 | + if( ix>=(sqlite3_uint64)LLONG_MAX ){ | |
| 4422 | + /* Get ix into signed i64 range. */ | |
| 4423 | + ix -= (sqlite3_uint64)LLONG_MAX; | |
| 4424 | + /* With 2's complement ALU, this next can be 1 step, but is split into | |
| 4425 | + * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ | |
| 4426 | + smBase += (LLONG_MAX/2) * smStep; | |
| 4427 | + smBase += (LLONG_MAX - LLONG_MAX/2) * smStep; | |
| 4428 | + } | |
| 4429 | + /* Under UBSAN (or on 1's complement machines), must do this last term | |
| 4430 | + * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ | |
| 4431 | + if( ix>=2 ){ | |
| 4432 | + sqlite3_int64 ix2 = (sqlite3_int64)ix/2; | |
| 4433 | + smBase += ix2*smStep; | |
| 4434 | + ix -= ix2; | |
| 4435 | + } | |
| 4436 | + return smBase + ((sqlite3_int64)ix)*smStep; | |
| 4437 | +} | |
| 4194 | 4438 | |
| 4439 | +/* typedef unsigned char u8; */ | |
| 4440 | + | |
| 4441 | +typedef struct SequenceSpec { | |
| 4442 | + sqlite3_int64 iBase; /* Starting value ("start") */ | |
| 4443 | + sqlite3_int64 iTerm; /* Given terminal value ("stop") */ | |
| 4444 | + sqlite3_int64 iStep; /* Increment ("step") */ | |
| 4445 | + sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */ | |
| 4446 | + sqlite3_uint64 uSeqIndexNow; /* Current index during generation */ | |
| 4447 | + sqlite3_int64 iValueNow; /* Current value during generation */ | |
| 4448 | + u8 isNotEOF; /* Sequence generation not exhausted */ | |
| 4449 | + u8 isReversing; /* Sequence is being reverse generated */ | |
| 4450 | +} SequenceSpec; | |
| 4451 | + | |
| 4452 | +/* | |
| 4453 | +** Prepare a SequenceSpec for use in generating an integer series | |
| 4454 | +** given initialized iBase, iTerm and iStep values. Sequence is | |
| 4455 | +** initialized per given isReversing. Other members are computed. | |
| 4456 | +*/ | |
| 4457 | +static void setupSequence( SequenceSpec *pss ){ | |
| 4458 | + int bSameSigns; | |
| 4459 | + pss->uSeqIndexMax = 0; | |
| 4460 | + pss->isNotEOF = 0; | |
| 4461 | + bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0); | |
| 4462 | + if( pss->iTerm < pss->iBase ){ | |
| 4463 | + sqlite3_uint64 nuspan = 0; | |
| 4464 | + if( bSameSigns ){ | |
| 4465 | + nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm); | |
| 4466 | + }else{ | |
| 4467 | + /* Under UBSAN (or on 1's complement machines), must do this in steps. | |
| 4468 | + * In this clause, iBase>=0 and iTerm<0 . */ | |
| 4469 | + nuspan = 1; | |
| 4470 | + nuspan += pss->iBase; | |
| 4471 | + nuspan += -(pss->iTerm+1); | |
| 4472 | + } | |
| 4473 | + if( pss->iStep<0 ){ | |
| 4474 | + pss->isNotEOF = 1; | |
| 4475 | + if( nuspan==ULONG_MAX ){ | |
| 4476 | + pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1; | |
| 4477 | + }else if( pss->iStep>LLONG_MIN ){ | |
| 4478 | + pss->uSeqIndexMax = nuspan/-pss->iStep; | |
| 4479 | + } | |
| 4480 | + } | |
| 4481 | + }else if( pss->iTerm > pss->iBase ){ | |
| 4482 | + sqlite3_uint64 puspan = 0; | |
| 4483 | + if( bSameSigns ){ | |
| 4484 | + puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase); | |
| 4485 | + }else{ | |
| 4486 | + /* Under UBSAN (or on 1's complement machines), must do this in steps. | |
| 4487 | + * In this clause, iTerm>=0 and iBase<0 . */ | |
| 4488 | + puspan = 1; | |
| 4489 | + puspan += pss->iTerm; | |
| 4490 | + puspan += -(pss->iBase+1); | |
| 4491 | + } | |
| 4492 | + if( pss->iStep>0 ){ | |
| 4493 | + pss->isNotEOF = 1; | |
| 4494 | + pss->uSeqIndexMax = puspan/pss->iStep; | |
| 4495 | + } | |
| 4496 | + }else if( pss->iTerm == pss->iBase ){ | |
| 4497 | + pss->isNotEOF = 1; | |
| 4498 | + pss->uSeqIndexMax = 0; | |
| 4499 | + } | |
| 4500 | + pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0; | |
| 4501 | + pss->iValueNow = (pss->isReversing) | |
| 4502 | + ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax) | |
| 4503 | + : pss->iBase; | |
| 4504 | +} | |
| 4505 | + | |
| 4506 | +/* | |
| 4507 | +** Progress sequence generator to yield next value, if any. | |
| 4508 | +** Leave its state to either yield next value or be at EOF. | |
| 4509 | +** Return whether there is a next value, or 0 at EOF. | |
| 4510 | +*/ | |
| 4511 | +static int progressSequence( SequenceSpec *pss ){ | |
| 4512 | + if( !pss->isNotEOF ) return 0; | |
| 4513 | + if( pss->isReversing ){ | |
| 4514 | + if( pss->uSeqIndexNow > 0 ){ | |
| 4515 | + pss->uSeqIndexNow--; | |
| 4516 | + pss->iValueNow -= pss->iStep; | |
| 4517 | + }else{ | |
| 4518 | + pss->isNotEOF = 0; | |
| 4519 | + } | |
| 4520 | + }else{ | |
| 4521 | + if( pss->uSeqIndexNow < pss->uSeqIndexMax ){ | |
| 4522 | + pss->uSeqIndexNow++; | |
| 4523 | + pss->iValueNow += pss->iStep; | |
| 4524 | + }else{ | |
| 4525 | + pss->isNotEOF = 0; | |
| 4526 | + } | |
| 4527 | + } | |
| 4528 | + return pss->isNotEOF; | |
| 4529 | +} | |
| 4195 | 4530 | |
| 4196 | 4531 | /* series_cursor is a subclass of sqlite3_vtab_cursor which will |
| 4197 | 4532 | ** serve as the underlying representation of a cursor that scans |
| 4198 | 4533 | ** over rows of the result |
| 4199 | 4534 | */ |
| 4200 | 4535 | typedef struct series_cursor series_cursor; |
| 4201 | 4536 | struct series_cursor { |
| 4202 | 4537 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 4203 | - int isDesc; /* True to count down rather than up */ | |
| 4204 | - sqlite3_int64 iRowid; /* The rowid */ | |
| 4205 | - sqlite3_int64 iValue; /* Current value ("value") */ | |
| 4206 | - sqlite3_int64 mnValue; /* Mimimum value ("start") */ | |
| 4207 | - sqlite3_int64 mxValue; /* Maximum value ("stop") */ | |
| 4208 | - sqlite3_int64 iStep; /* Increment ("step") */ | |
| 4538 | + SequenceSpec ss; /* (this) Derived class data */ | |
| 4209 | 4539 | }; |
| 4210 | 4540 | |
| 4211 | 4541 | /* |
| 4212 | 4542 | ** The seriesConnect() method is invoked to create a new |
| 4213 | 4543 | ** series_vtab that describes the generate_series virtual table. |
| @@ -4285,16 +4615,11 @@ | ||
| 4285 | 4615 | /* |
| 4286 | 4616 | ** Advance a series_cursor to its next row of output. |
| 4287 | 4617 | */ |
| 4288 | 4618 | static int seriesNext(sqlite3_vtab_cursor *cur){ |
| 4289 | 4619 | series_cursor *pCur = (series_cursor*)cur; |
| 4290 | - if( pCur->isDesc ){ | |
| 4291 | - pCur->iValue -= pCur->iStep; | |
| 4292 | - }else{ | |
| 4293 | - pCur->iValue += pCur->iStep; | |
| 4294 | - } | |
| 4295 | - pCur->iRowid++; | |
| 4620 | + progressSequence( & pCur->ss ); | |
| 4296 | 4621 | return SQLITE_OK; |
| 4297 | 4622 | } |
| 4298 | 4623 | |
| 4299 | 4624 | /* |
| 4300 | 4625 | ** Return values of columns for the row at which the series_cursor |
| @@ -4306,44 +4631,40 @@ | ||
| 4306 | 4631 | int i /* Which column to return */ |
| 4307 | 4632 | ){ |
| 4308 | 4633 | series_cursor *pCur = (series_cursor*)cur; |
| 4309 | 4634 | sqlite3_int64 x = 0; |
| 4310 | 4635 | switch( i ){ |
| 4311 | - case SERIES_COLUMN_START: x = pCur->mnValue; break; | |
| 4312 | - case SERIES_COLUMN_STOP: x = pCur->mxValue; break; | |
| 4313 | - case SERIES_COLUMN_STEP: x = pCur->iStep; break; | |
| 4314 | - default: x = pCur->iValue; break; | |
| 4636 | + case SERIES_COLUMN_START: x = pCur->ss.iBase; break; | |
| 4637 | + case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break; | |
| 4638 | + case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break; | |
| 4639 | + default: x = pCur->ss.iValueNow; break; | |
| 4315 | 4640 | } |
| 4316 | 4641 | sqlite3_result_int64(ctx, x); |
| 4317 | 4642 | return SQLITE_OK; |
| 4318 | 4643 | } |
| 4319 | 4644 | |
| 4320 | 4645 | /* |
| 4321 | -** Return the rowid for the current row. In this implementation, the | |
| 4322 | -** first row returned is assigned rowid value 1, and each subsequent | |
| 4323 | -** row a value 1 more than that of the previous. | |
| 4646 | +** Return the rowid for the current row, logically equivalent to n+1 where | |
| 4647 | +** "n" is the ascending integer in the aforesaid production definition. | |
| 4324 | 4648 | */ |
| 4325 | 4649 | static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| 4326 | 4650 | series_cursor *pCur = (series_cursor*)cur; |
| 4327 | - *pRowid = pCur->iRowid; | |
| 4651 | + sqlite3_uint64 n = pCur->ss.uSeqIndexNow; | |
| 4652 | + *pRowid = (sqlite3_int64)((n<0xffffffffffffffff)? n+1 : 0); | |
| 4328 | 4653 | return SQLITE_OK; |
| 4329 | 4654 | } |
| 4330 | 4655 | |
| 4331 | 4656 | /* |
| 4332 | 4657 | ** Return TRUE if the cursor has been moved off of the last |
| 4333 | 4658 | ** row of output. |
| 4334 | 4659 | */ |
| 4335 | 4660 | static int seriesEof(sqlite3_vtab_cursor *cur){ |
| 4336 | 4661 | series_cursor *pCur = (series_cursor*)cur; |
| 4337 | - if( pCur->isDesc ){ | |
| 4338 | - return pCur->iValue < pCur->mnValue; | |
| 4339 | - }else{ | |
| 4340 | - return pCur->iValue > pCur->mxValue; | |
| 4341 | - } | |
| 4662 | + return !pCur->ss.isNotEOF; | |
| 4342 | 4663 | } |
| 4343 | 4664 | |
| 4344 | -/* True to cause run-time checking of the start=, stop=, and/or step= | |
| 4665 | +/* True to cause run-time checking of the start=, stop=, and/or step= | |
| 4345 | 4666 | ** parameters. The only reason to do this is for testing the |
| 4346 | 4667 | ** constraint checking logic for virtual tables in the SQLite core. |
| 4347 | 4668 | */ |
| 4348 | 4669 | #ifndef SQLITE_SERIES_CONSTRAINT_VERIFY |
| 4349 | 4670 | # define SQLITE_SERIES_CONSTRAINT_VERIFY 0 |
| @@ -4350,11 +4671,11 @@ | ||
| 4350 | 4671 | #endif |
| 4351 | 4672 | |
| 4352 | 4673 | /* |
| 4353 | 4674 | ** This method is called to "rewind" the series_cursor object back |
| 4354 | 4675 | ** to the first row of output. This method is always called at least |
| 4355 | -** once prior to any call to seriesColumn() or seriesRowid() or | |
| 4676 | +** once prior to any call to seriesColumn() or seriesRowid() or | |
| 4356 | 4677 | ** seriesEof(). |
| 4357 | 4678 | ** |
| 4358 | 4679 | ** The query plan selected by seriesBestIndex is passed in the idxNum |
| 4359 | 4680 | ** parameter. (idxStr is not used in this implementation.) idxNum |
| 4360 | 4681 | ** is a bitmask showing which constraints are available: |
| @@ -4370,58 +4691,53 @@ | ||
| 4370 | 4691 | ** This routine should initialize the cursor and position it so that it |
| 4371 | 4692 | ** is pointing at the first row, or pointing off the end of the table |
| 4372 | 4693 | ** (so that seriesEof() will return true) if the table is empty. |
| 4373 | 4694 | */ |
| 4374 | 4695 | static int seriesFilter( |
| 4375 | - sqlite3_vtab_cursor *pVtabCursor, | |
| 4696 | + sqlite3_vtab_cursor *pVtabCursor, | |
| 4376 | 4697 | int idxNum, const char *idxStrUnused, |
| 4377 | 4698 | int argc, sqlite3_value **argv |
| 4378 | 4699 | ){ |
| 4379 | 4700 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 4380 | 4701 | int i = 0; |
| 4381 | 4702 | (void)idxStrUnused; |
| 4382 | 4703 | if( idxNum & 1 ){ |
| 4383 | - pCur->mnValue = sqlite3_value_int64(argv[i++]); | |
| 4704 | + pCur->ss.iBase = sqlite3_value_int64(argv[i++]); | |
| 4384 | 4705 | }else{ |
| 4385 | - pCur->mnValue = 0; | |
| 4706 | + pCur->ss.iBase = 0; | |
| 4386 | 4707 | } |
| 4387 | 4708 | if( idxNum & 2 ){ |
| 4388 | - pCur->mxValue = sqlite3_value_int64(argv[i++]); | |
| 4709 | + pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); | |
| 4389 | 4710 | }else{ |
| 4390 | - pCur->mxValue = 0xffffffff; | |
| 4711 | + pCur->ss.iTerm = 0xffffffff; | |
| 4391 | 4712 | } |
| 4392 | 4713 | if( idxNum & 4 ){ |
| 4393 | - pCur->iStep = sqlite3_value_int64(argv[i++]); | |
| 4394 | - if( pCur->iStep==0 ){ | |
| 4395 | - pCur->iStep = 1; | |
| 4396 | - }else if( pCur->iStep<0 ){ | |
| 4397 | - pCur->iStep = -pCur->iStep; | |
| 4714 | + pCur->ss.iStep = sqlite3_value_int64(argv[i++]); | |
| 4715 | + if( pCur->ss.iStep==0 ){ | |
| 4716 | + pCur->ss.iStep = 1; | |
| 4717 | + }else if( pCur->ss.iStep<0 ){ | |
| 4398 | 4718 | if( (idxNum & 16)==0 ) idxNum |= 8; |
| 4399 | 4719 | } |
| 4400 | 4720 | }else{ |
| 4401 | - pCur->iStep = 1; | |
| 4721 | + pCur->ss.iStep = 1; | |
| 4402 | 4722 | } |
| 4403 | 4723 | for(i=0; i<argc; i++){ |
| 4404 | 4724 | if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ |
| 4405 | 4725 | /* If any of the constraints have a NULL value, then return no rows. |
| 4406 | 4726 | ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ |
| 4407 | - pCur->mnValue = 1; | |
| 4408 | - pCur->mxValue = 0; | |
| 4727 | + pCur->ss.iBase = 1; | |
| 4728 | + pCur->ss.iTerm = 0; | |
| 4729 | + pCur->ss.iStep = 1; | |
| 4409 | 4730 | break; |
| 4410 | 4731 | } |
| 4411 | 4732 | } |
| 4412 | 4733 | if( idxNum & 8 ){ |
| 4413 | - pCur->isDesc = 1; | |
| 4414 | - pCur->iValue = pCur->mxValue; | |
| 4415 | - if( pCur->iStep>0 ){ | |
| 4416 | - pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep; | |
| 4417 | - } | |
| 4734 | + pCur->ss.isReversing = pCur->ss.iStep > 0; | |
| 4418 | 4735 | }else{ |
| 4419 | - pCur->isDesc = 0; | |
| 4420 | - pCur->iValue = pCur->mnValue; | |
| 4736 | + pCur->ss.isReversing = pCur->ss.iStep < 0; | |
| 4421 | 4737 | } |
| 4422 | - pCur->iRowid = 1; | |
| 4738 | + setupSequence( &pCur->ss ); | |
| 4423 | 4739 | return SQLITE_OK; |
| 4424 | 4740 | } |
| 4425 | 4741 | |
| 4426 | 4742 | /* |
| 4427 | 4743 | ** SQLite will invoke this method one or more times while planning a query |
| @@ -5172,11 +5488,11 @@ | ||
| 5172 | 5488 | re_append(p, RE_OP_FORK, -sz); |
| 5173 | 5489 | } |
| 5174 | 5490 | break; |
| 5175 | 5491 | } |
| 5176 | 5492 | case '[': { |
| 5177 | - int iFirst = p->nState; | |
| 5493 | + unsigned int iFirst = p->nState; | |
| 5178 | 5494 | if( rePeek(p)=='^' ){ |
| 5179 | 5495 | re_append(p, RE_OP_CC_EXC, 0); |
| 5180 | 5496 | p->sIn.i++; |
| 5181 | 5497 | }else{ |
| 5182 | 5498 | re_append(p, RE_OP_CC_INC, 0); |
| @@ -5196,11 +5512,11 @@ | ||
| 5196 | 5512 | re_append(p, RE_OP_CC_VALUE, c); |
| 5197 | 5513 | } |
| 5198 | 5514 | if( rePeek(p)==']' ){ p->sIn.i++; break; } |
| 5199 | 5515 | } |
| 5200 | 5516 | if( c==0 ) return "unclosed '['"; |
| 5201 | - p->aArg[iFirst] = p->nState - iFirst; | |
| 5517 | + if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst; | |
| 5202 | 5518 | break; |
| 5203 | 5519 | } |
| 5204 | 5520 | case '\\': { |
| 5205 | 5521 | int specialOp = 0; |
| 5206 | 5522 | switch( rePeek(p) ){ |
| @@ -9213,13 +9529,23 @@ | ||
| 9213 | 9529 | ** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry |
| 9214 | 9530 | ** linked list. Remove it from the list and free the object. |
| 9215 | 9531 | */ |
| 9216 | 9532 | static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ |
| 9217 | 9533 | if( pOld ){ |
| 9218 | - ZipfileEntry **pp; | |
| 9219 | - for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); | |
| 9220 | - *pp = (*pp)->pNext; | |
| 9534 | + if( pTab->pFirstEntry==pOld ){ | |
| 9535 | + pTab->pFirstEntry = pOld->pNext; | |
| 9536 | + if( pTab->pLastEntry==pOld ) pTab->pLastEntry = 0; | |
| 9537 | + }else{ | |
| 9538 | + ZipfileEntry *p; | |
| 9539 | + for(p=pTab->pFirstEntry; p; p=p->pNext){ | |
| 9540 | + if( p->pNext==pOld ){ | |
| 9541 | + p->pNext = pOld->pNext; | |
| 9542 | + if( pTab->pLastEntry==pOld ) pTab->pLastEntry = p; | |
| 9543 | + break; | |
| 9544 | + } | |
| 9545 | + } | |
| 9546 | + } | |
| 9221 | 9547 | zipfileEntryFree(pOld); |
| 9222 | 9548 | } |
| 9223 | 9549 | } |
| 9224 | 9550 | |
| 9225 | 9551 | /* |
| @@ -12510,10 +12836,11 @@ | ||
| 12510 | 12836 | #endif |
| 12511 | 12837 | |
| 12512 | 12838 | #endif /* ifndef _SQLITE_RECOVER_H */ |
| 12513 | 12839 | |
| 12514 | 12840 | /************************* End ../ext/recover/sqlite3recover.h ********************/ |
| 12841 | +# ifndef SQLITE_HAVE_SQLITE3R | |
| 12515 | 12842 | /************************* Begin ../ext/recover/dbdata.c ******************/ |
| 12516 | 12843 | /* |
| 12517 | 12844 | ** 2019-04-17 |
| 12518 | 12845 | ** |
| 12519 | 12846 | ** The author disclaims copyright to this source code. In place of |
| @@ -14576,11 +14903,11 @@ | ||
| 14576 | 14903 | pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); |
| 14577 | 14904 | while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 14578 | 14905 | int iField = sqlite3_column_int(pStmt, 0); |
| 14579 | 14906 | int iCol = sqlite3_column_int(pStmt, 1); |
| 14580 | 14907 | |
| 14581 | - assert( iField<pNew->nCol && iCol<pNew->nCol ); | |
| 14908 | + assert( iCol<pNew->nCol ); | |
| 14582 | 14909 | pNew->aCol[iCol].iField = iField; |
| 14583 | 14910 | |
| 14584 | 14911 | pNew->bIntkey = 0; |
| 14585 | 14912 | iPk = -2; |
| 14586 | 14913 | } |
| @@ -16340,10 +16667,11 @@ | ||
| 16340 | 16667 | } |
| 16341 | 16668 | |
| 16342 | 16669 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 16343 | 16670 | |
| 16344 | 16671 | /************************* End ../ext/recover/sqlite3recover.c ********************/ |
| 16672 | +# endif /* SQLITE_HAVE_SQLITE3R */ | |
| 16345 | 16673 | #endif |
| 16346 | 16674 | #ifdef SQLITE_SHELL_EXTSRC |
| 16347 | 16675 | # include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC) |
| 16348 | 16676 | #endif |
| 16349 | 16677 | |
| @@ -16521,10 +16849,11 @@ | ||
| 16521 | 16849 | #define SHFLG_CountChanges 0x00000020 /* .changes setting */ |
| 16522 | 16850 | #define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */ |
| 16523 | 16851 | #define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ |
| 16524 | 16852 | #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ |
| 16525 | 16853 | #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ |
| 16854 | +#define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */ | |
| 16526 | 16855 | |
| 16527 | 16856 | /* |
| 16528 | 16857 | ** Macros for testing and setting shellFlgs |
| 16529 | 16858 | */ |
| 16530 | 16859 | #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) |
| @@ -16764,10 +17093,11 @@ | ||
| 16764 | 17093 | if( hasCRNL ){ |
| 16765 | 17094 | /* If the original contains \r\n then do no conversions back to \n */ |
| 16766 | 17095 | }else{ |
| 16767 | 17096 | /* If the file did not originally contain \r\n then convert any new |
| 16768 | 17097 | ** \r\n back into \n */ |
| 17098 | + p[sz] = 0; | |
| 16769 | 17099 | for(i=j=0; i<sz; i++){ |
| 16770 | 17100 | if( p[i]=='\r' && p[i+1]=='\n' ) i++; |
| 16771 | 17101 | p[j++] = p[i]; |
| 16772 | 17102 | } |
| 16773 | 17103 | sz = j; |
| @@ -16854,10 +17184,11 @@ | ||
| 16854 | 17184 | */ |
| 16855 | 17185 | static void output_quoted_string(FILE *out, const char *z){ |
| 16856 | 17186 | int i; |
| 16857 | 17187 | char c; |
| 16858 | 17188 | setBinaryMode(out, 1); |
| 17189 | + if( z==0 ) return; | |
| 16859 | 17190 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 16860 | 17191 | if( c==0 ){ |
| 16861 | 17192 | utf8_printf(out,"'%s'",z); |
| 16862 | 17193 | }else{ |
| 16863 | 17194 | raw_printf(out, "'"); |
| @@ -16983,10 +17314,11 @@ | ||
| 16983 | 17314 | /* |
| 16984 | 17315 | ** Output the given string as a quoted according to JSON quoting rules. |
| 16985 | 17316 | */ |
| 16986 | 17317 | static void output_json_string(FILE *out, const char *z, i64 n){ |
| 16987 | 17318 | unsigned int c; |
| 17319 | + if( z==0 ) z = ""; | |
| 16988 | 17320 | if( n<0 ) n = strlen(z); |
| 16989 | 17321 | fputc('"', out); |
| 16990 | 17322 | while( n-- ){ |
| 16991 | 17323 | c = *(z++); |
| 16992 | 17324 | if( c=='\\' || c=='"' ){ |
| @@ -17107,12 +17439,11 @@ | ||
| 17107 | 17439 | /* |
| 17108 | 17440 | ** This routine runs when the user presses Ctrl-C |
| 17109 | 17441 | */ |
| 17110 | 17442 | static void interrupt_handler(int NotUsed){ |
| 17111 | 17443 | UNUSED_PARAMETER(NotUsed); |
| 17112 | - seenInterrupt++; | |
| 17113 | - if( seenInterrupt>2 ) exit(1); | |
| 17444 | + if( ++seenInterrupt>1 ) exit(1); | |
| 17114 | 17445 | if( globalDb ) sqlite3_interrupt(globalDb); |
| 17115 | 17446 | } |
| 17116 | 17447 | |
| 17117 | 17448 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 17118 | 17449 | /* |
| @@ -18559,14 +18890,18 @@ | ||
| 18559 | 18890 | zVar = zNum; |
| 18560 | 18891 | } |
| 18561 | 18892 | sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC); |
| 18562 | 18893 | if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){ |
| 18563 | 18894 | sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0)); |
| 18895 | +#ifdef NAN | |
| 18564 | 18896 | }else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){ |
| 18565 | 18897 | sqlite3_bind_double(pStmt, i, NAN); |
| 18898 | +#endif | |
| 18899 | +#ifdef INFINITY | |
| 18566 | 18900 | }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ |
| 18567 | 18901 | sqlite3_bind_double(pStmt, i, INFINITY); |
| 18902 | +#endif | |
| 18568 | 18903 | }else{ |
| 18569 | 18904 | sqlite3_bind_null(pStmt, i); |
| 18570 | 18905 | } |
| 18571 | 18906 | sqlite3_reset(pQ); |
| 18572 | 18907 | } |
| @@ -18806,11 +19141,11 @@ | ||
| 18806 | 19141 | nAlloc = nColumn*4; |
| 18807 | 19142 | if( nAlloc<=0 ) nAlloc = 1; |
| 18808 | 19143 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 18809 | 19144 | shell_check_oom(azData); |
| 18810 | 19145 | azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 18811 | - shell_check_oom((void*)azNextLine); | |
| 19146 | + shell_check_oom(azNextLine); | |
| 18812 | 19147 | memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
| 18813 | 19148 | if( p->cmOpts.bQuote ){ |
| 18814 | 19149 | azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 18815 | 19150 | shell_check_oom(azQuoted); |
| 18816 | 19151 | memset(azQuoted, 0, nColumn*sizeof(char*) ); |
| @@ -18836,10 +19171,11 @@ | ||
| 18836 | 19171 | if( wx==0 ){ |
| 18837 | 19172 | wx = p->cmOpts.iWrap; |
| 18838 | 19173 | } |
| 18839 | 19174 | if( wx<0 ) wx = -wx; |
| 18840 | 19175 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 19176 | + if( uz==0 ) uz = (u8*)""; | |
| 18841 | 19177 | azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); |
| 18842 | 19178 | } |
| 18843 | 19179 | do{ |
| 18844 | 19180 | int useNextLine = bNextLine; |
| 18845 | 19181 | bNextLine = 0; |
| @@ -19769,17 +20105,17 @@ | ||
| 19769 | 20105 | " from the \".mode\" output mode", |
| 19770 | 20106 | " * If FILE begins with \"|\" then it is a command that generates the", |
| 19771 | 20107 | " input text.", |
| 19772 | 20108 | #endif |
| 19773 | 20109 | #ifndef SQLITE_OMIT_TEST_CONTROL |
| 19774 | - ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", | |
| 20110 | + ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", | |
| 19775 | 20111 | #endif |
| 19776 | 20112 | ".indexes ?TABLE? Show names of indexes", |
| 19777 | 20113 | " If TABLE is specified, only show indexes for", |
| 19778 | 20114 | " tables matching TABLE using the LIKE operator.", |
| 19779 | 20115 | #ifdef SQLITE_ENABLE_IOTRACE |
| 19780 | - ".iotrace FILE Enable I/O diagnostic logging to FILE", | |
| 20116 | + ",iotrace FILE Enable I/O diagnostic logging to FILE", | |
| 19781 | 20117 | #endif |
| 19782 | 20118 | ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", |
| 19783 | 20119 | ".lint OPTIONS Report potential schema issues.", |
| 19784 | 20120 | " Options:", |
| 19785 | 20121 | " fkey-indexes Find missing foreign key indexes", |
| @@ -19884,11 +20220,11 @@ | ||
| 19884 | 20220 | ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", |
| 19885 | 20221 | ".schema ?PATTERN? Show the CREATE statements matching PATTERN", |
| 19886 | 20222 | " Options:", |
| 19887 | 20223 | " --indent Try to pretty-print the schema", |
| 19888 | 20224 | " --nosys Omit objects whose names start with \"sqlite_\"", |
| 19889 | - ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", | |
| 20225 | + ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", | |
| 19890 | 20226 | " Options:", |
| 19891 | 20227 | " --init Create a new SELFTEST table", |
| 19892 | 20228 | " -v Verbose output", |
| 19893 | 20229 | ".separator COL ?ROW? Change the column and row separators", |
| 19894 | 20230 | #if defined(SQLITE_ENABLE_SESSION) |
| @@ -19926,13 +20262,13 @@ | ||
| 19926 | 20262 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 19927 | 20263 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 19928 | 20264 | #endif |
| 19929 | 20265 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 19930 | 20266 | #ifndef SQLITE_SHELL_FIDDLE |
| 19931 | - ".testcase NAME Begin redirecting output to 'testcase-out.txt'", | |
| 20267 | + ",testcase NAME Begin redirecting output to 'testcase-out.txt'", | |
| 19932 | 20268 | #endif |
| 19933 | - ".testctrl CMD ... Run various sqlite3_test_control() operations", | |
| 20269 | + ",testctrl CMD ... Run various sqlite3_test_control() operations", | |
| 19934 | 20270 | " Run \".testctrl\" with no arguments for details", |
| 19935 | 20271 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 19936 | 20272 | ".timer on|off Turn SQL timer on or off", |
| 19937 | 20273 | #ifndef SQLITE_OMIT_TRACE |
| 19938 | 20274 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| @@ -19980,20 +20316,45 @@ | ||
| 19980 | 20316 | || zPattern[0]=='0' |
| 19981 | 20317 | || cli_strcmp(zPattern,"-a")==0 |
| 19982 | 20318 | || cli_strcmp(zPattern,"-all")==0 |
| 19983 | 20319 | || cli_strcmp(zPattern,"--all")==0 |
| 19984 | 20320 | ){ |
| 19985 | - /* Show all commands, but only one line per command */ | |
| 19986 | - if( zPattern==0 ) zPattern = ""; | |
| 20321 | + enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 }; | |
| 20322 | + enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 }; | |
| 20323 | + /* Show all or most commands | |
| 20324 | + ** *zPattern==0 => summary of documented commands only | |
| 20325 | + ** *zPattern=='0' => whole help for undocumented commands | |
| 20326 | + ** Otherwise => whole help for documented commands | |
| 20327 | + */ | |
| 20328 | + enum HelpWanted hw = HW_SummaryOnly; | |
| 20329 | + enum HelpHave hh = HH_More; | |
| 20330 | + if( zPattern!=0 ){ | |
| 20331 | + hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull; | |
| 20332 | + } | |
| 19987 | 20333 | for(i=0; i<ArraySize(azHelp); i++){ |
| 19988 | - if( azHelp[i][0]=='.' || zPattern[0] ){ | |
| 19989 | - utf8_printf(out, "%s\n", azHelp[i]); | |
| 19990 | - n++; | |
| 20334 | + switch( azHelp[i][0] ){ | |
| 20335 | + case ',': | |
| 20336 | + hh = HH_Summary|HH_Undoc; | |
| 20337 | + break; | |
| 20338 | + case '.': | |
| 20339 | + hh = HH_Summary; | |
| 20340 | + break; | |
| 20341 | + default: | |
| 20342 | + hh &= ~HH_Summary; | |
| 20343 | + break; | |
| 20344 | + } | |
| 20345 | + if( ((hw^hh)&HH_Undoc)==0 ){ | |
| 20346 | + if( (hh&HH_Summary)!=0 ){ | |
| 20347 | + utf8_printf(out, ".%s\n", azHelp[i]+1); | |
| 20348 | + ++n; | |
| 20349 | + }else if( (hw&HW_SummaryOnly)==0 ){ | |
| 20350 | + utf8_printf(out, "%s\n", azHelp[i]); | |
| 20351 | + } | |
| 19991 | 20352 | } |
| 19992 | 20353 | } |
| 19993 | 20354 | }else{ |
| 19994 | - /* Look for commands that for which zPattern is an exact prefix */ | |
| 20355 | + /* Seek documented commands for which zPattern is an exact prefix */ | |
| 19995 | 20356 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 19996 | 20357 | shell_check_oom(zPat); |
| 19997 | 20358 | for(i=0; i<ArraySize(azHelp); i++){ |
| 19998 | 20359 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 19999 | 20360 | utf8_printf(out, "%s\n", azHelp[i]); |
| @@ -20002,28 +20363,32 @@ | ||
| 20002 | 20363 | } |
| 20003 | 20364 | } |
| 20004 | 20365 | sqlite3_free(zPat); |
| 20005 | 20366 | if( n ){ |
| 20006 | 20367 | if( n==1 ){ |
| 20007 | - /* when zPattern is a prefix of exactly one command, then include the | |
| 20008 | - ** details of that command, which should begin at offset j */ | |
| 20009 | - while( j<ArraySize(azHelp)-1 && azHelp[j][0]!='.' ){ | |
| 20368 | + /* when zPattern is a prefix of exactly one command, then include | |
| 20369 | + ** the details of that command, which should begin at offset j */ | |
| 20370 | + while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ | |
| 20010 | 20371 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20011 | 20372 | j++; |
| 20012 | 20373 | } |
| 20013 | 20374 | } |
| 20014 | 20375 | return n; |
| 20015 | 20376 | } |
| 20016 | - /* Look for commands that contain zPattern anywhere. Show the complete | |
| 20017 | - ** text of all commands that match. */ | |
| 20377 | + /* Look for documented commands that contain zPattern anywhere. | |
| 20378 | + ** Show complete text of all documented commands that match. */ | |
| 20018 | 20379 | zPat = sqlite3_mprintf("%%%s%%", zPattern); |
| 20019 | 20380 | shell_check_oom(zPat); |
| 20020 | 20381 | for(i=0; i<ArraySize(azHelp); i++){ |
| 20382 | + if( azHelp[i][0]==',' ){ | |
| 20383 | + while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; | |
| 20384 | + continue; | |
| 20385 | + } | |
| 20021 | 20386 | if( azHelp[i][0]=='.' ) j = i; |
| 20022 | 20387 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 20023 | 20388 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20024 | - while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]!='.' ){ | |
| 20389 | + while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ | |
| 20025 | 20390 | j++; |
| 20026 | 20391 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20027 | 20392 | } |
| 20028 | 20393 | i = j; |
| 20029 | 20394 | n++; |
| @@ -20337,17 +20702,34 @@ | ||
| 20337 | 20702 | } |
| 20338 | 20703 | globalDb = p->db; |
| 20339 | 20704 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 20340 | 20705 | utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 20341 | 20706 | zDbFilename, sqlite3_errmsg(p->db)); |
| 20342 | - if( openFlags & OPEN_DB_KEEPALIVE ){ | |
| 20343 | - sqlite3_open(":memory:", &p->db); | |
| 20344 | - return; | |
| 20707 | + if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ | |
| 20708 | + exit(1); | |
| 20345 | 20709 | } |
| 20346 | - exit(1); | |
| 20710 | + sqlite3_close(p->db); | |
| 20711 | + sqlite3_open(":memory:", &p->db); | |
| 20712 | + if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ | |
| 20713 | + utf8_printf(stderr, | |
| 20714 | + "Also: unable to open substitute in-memory database.\n" | |
| 20715 | + ); | |
| 20716 | + exit(1); | |
| 20717 | + }else{ | |
| 20718 | + utf8_printf(stderr, | |
| 20719 | + "Notice: using substitute in-memory database instead of \"%s\"\n", | |
| 20720 | + zDbFilename); | |
| 20721 | + } | |
| 20347 | 20722 | } |
| 20348 | 20723 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| 20724 | + | |
| 20725 | + /* Reflect the use or absence of --unsafe-testing invocation. */ | |
| 20726 | + { | |
| 20727 | + int testmode_on = ShellHasFlag(p,SHFLG_TestingMode); | |
| 20728 | + sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0); | |
| 20729 | + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0); | |
| 20730 | + } | |
| 20349 | 20731 | |
| 20350 | 20732 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 20351 | 20733 | sqlite3_enable_load_extension(p->db, 1); |
| 20352 | 20734 | #endif |
| 20353 | 20735 | sqlite3_shathree_init(p->db, 0, 0); |
| @@ -20356,13 +20738,10 @@ | ||
| 20356 | 20738 | sqlite3_base64_init(p->db, 0, 0); |
| 20357 | 20739 | sqlite3_base85_init(p->db, 0, 0); |
| 20358 | 20740 | sqlite3_regexp_init(p->db, 0, 0); |
| 20359 | 20741 | sqlite3_ieee_init(p->db, 0, 0); |
| 20360 | 20742 | sqlite3_series_init(p->db, 0, 0); |
| 20361 | -#if SQLITE_SHELL_HAVE_RECOVER | |
| 20362 | - sqlite3_dbdata_init(p->db, 0, 0); | |
| 20363 | -#endif | |
| 20364 | 20743 | #ifndef SQLITE_SHELL_FIDDLE |
| 20365 | 20744 | sqlite3_fileio_init(p->db, 0, 0); |
| 20366 | 20745 | sqlite3_completion_init(p->db, 0, 0); |
| 20367 | 20746 | #endif |
| 20368 | 20747 | #ifdef SQLITE_HAVE_ZLIB |
| @@ -20550,10 +20929,11 @@ | ||
| 20550 | 20929 | ** \s -> space |
| 20551 | 20930 | ** \" -> " |
| 20552 | 20931 | ** \' -> ' |
| 20553 | 20932 | ** \\ -> backslash |
| 20554 | 20933 | ** \NNN -> ascii character NNN in octal |
| 20934 | +** \xHH -> ascii character HH in hexadecimal | |
| 20555 | 20935 | */ |
| 20556 | 20936 | static void resolve_backslashes(char *z){ |
| 20557 | 20937 | int i, j; |
| 20558 | 20938 | char c; |
| 20559 | 20939 | while( *z && *z!='\\' ) z++; |
| @@ -20578,10 +20958,19 @@ | ||
| 20578 | 20958 | c = '"'; |
| 20579 | 20959 | }else if( c=='\'' ){ |
| 20580 | 20960 | c = '\''; |
| 20581 | 20961 | }else if( c=='\\' ){ |
| 20582 | 20962 | c = '\\'; |
| 20963 | + }else if( c=='x' ){ | |
| 20964 | + int nhd = 0, hdv; | |
| 20965 | + u8 hv = 0; | |
| 20966 | + while( nhd<2 && (c=z[i+1+nhd])!=0 && (hdv=hexDigitValue(c))>=0 ){ | |
| 20967 | + hv = (u8)((hv<<4)|hdv); | |
| 20968 | + ++nhd; | |
| 20969 | + } | |
| 20970 | + i += nhd; | |
| 20971 | + c = (u8)hv; | |
| 20583 | 20972 | }else if( c>='0' && c<='7' ){ |
| 20584 | 20973 | c -= '0'; |
| 20585 | 20974 | if( z[i+1]>='0' && z[i+1]<='7' ){ |
| 20586 | 20975 | i++; |
| 20587 | 20976 | c = (c<<3) + z[i] - '0'; |
| @@ -20677,11 +21066,11 @@ | ||
| 20677 | 21066 | if( p->traceOut==0 ) return 0; |
| 20678 | 21067 | if( mType==SQLITE_TRACE_CLOSE ){ |
| 20679 | 21068 | utf8_printf(p->traceOut, "-- closing database connection\n"); |
| 20680 | 21069 | return 0; |
| 20681 | 21070 | } |
| 20682 | - if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){ | |
| 21071 | + if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){ | |
| 20683 | 21072 | zSql = (const char*)pX; |
| 20684 | 21073 | }else{ |
| 20685 | 21074 | pStmt = (sqlite3_stmt*)pP; |
| 20686 | 21075 | switch( p->eTraceType ){ |
| 20687 | 21076 | case SHELL_TRACE_EXPANDED: { |
| @@ -20709,11 +21098,11 @@ | ||
| 20709 | 21098 | case SQLITE_TRACE_STMT: { |
| 20710 | 21099 | utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 20711 | 21100 | break; |
| 20712 | 21101 | } |
| 20713 | 21102 | case SQLITE_TRACE_PROFILE: { |
| 20714 | - sqlite3_int64 nNanosec = *(sqlite3_int64*)pX; | |
| 21103 | + sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; | |
| 20715 | 21104 | utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 20716 | 21105 | break; |
| 20717 | 21106 | } |
| 20718 | 21107 | } |
| 20719 | 21108 | return 0; |
| @@ -20785,12 +21174,12 @@ | ||
| 20785 | 21174 | ** EOF on end-of-file. |
| 20786 | 21175 | ** + Report syntax errors on stderr |
| 20787 | 21176 | */ |
| 20788 | 21177 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 20789 | 21178 | int c; |
| 20790 | - int cSep = p->cColSep; | |
| 20791 | - int rSep = p->cRowSep; | |
| 21179 | + int cSep = (u8)p->cColSep; | |
| 21180 | + int rSep = (u8)p->cRowSep; | |
| 20792 | 21181 | p->n = 0; |
| 20793 | 21182 | c = fgetc(p->in); |
| 20794 | 21183 | if( c==EOF || seenInterrupt ){ |
| 20795 | 21184 | p->cTerm = EOF; |
| 20796 | 21185 | return 0; |
| @@ -20875,12 +21264,12 @@ | ||
| 20875 | 21264 | ** EOF on end-of-file. |
| 20876 | 21265 | ** + Report syntax errors on stderr |
| 20877 | 21266 | */ |
| 20878 | 21267 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 20879 | 21268 | int c; |
| 20880 | - int cSep = p->cColSep; | |
| 20881 | - int rSep = p->cRowSep; | |
| 21269 | + int cSep = (u8)p->cColSep; | |
| 21270 | + int rSep = (u8)p->cRowSep; | |
| 20882 | 21271 | p->n = 0; |
| 20883 | 21272 | c = fgetc(p->in); |
| 20884 | 21273 | if( c==EOF || seenInterrupt ){ |
| 20885 | 21274 | p->cTerm = EOF; |
| 20886 | 21275 | return 0; |
| @@ -21070,11 +21459,11 @@ | ||
| 21070 | 21459 | } |
| 21071 | 21460 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 21072 | 21461 | zName = sqlite3_column_text(pQuery, 0); |
| 21073 | 21462 | zSql = sqlite3_column_text(pQuery, 1); |
| 21074 | 21463 | if( zName==0 || zSql==0 ) continue; |
| 21075 | - if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ) continue; | |
| 21464 | + if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; | |
| 21076 | 21465 | printf("%s... ", zName); fflush(stdout); |
| 21077 | 21466 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 21078 | 21467 | if( zErrMsg ){ |
| 21079 | 21468 | utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 21080 | 21469 | sqlite3_free(zErrMsg); |
| @@ -21237,11 +21626,13 @@ | ||
| 21237 | 21626 | } |
| 21238 | 21627 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 21239 | 21628 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| 21240 | 21629 | && sqlite3_column_bytes(pStmt,0)>100 |
| 21241 | 21630 | ){ |
| 21242 | - memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100); | |
| 21631 | + const u8 *pb = sqlite3_column_blob(pStmt,0); | |
| 21632 | + shell_check_oom(pb); | |
| 21633 | + memcpy(aHdr, pb, 100); | |
| 21243 | 21634 | sqlite3_finalize(pStmt); |
| 21244 | 21635 | }else{ |
| 21245 | 21636 | raw_printf(stderr, "unable to read database header\n"); |
| 21246 | 21637 | sqlite3_finalize(pStmt); |
| 21247 | 21638 | return 1; |
| @@ -22054,11 +22445,14 @@ | ||
| 22054 | 22445 | if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR; |
| 22055 | 22446 | } |
| 22056 | 22447 | } |
| 22057 | 22448 | } |
| 22058 | 22449 | } |
| 22059 | - | |
| 22450 | + if( pAr->eCmd==0 ){ | |
| 22451 | + utf8_printf(stderr, "Required argument missing. Usage:\n"); | |
| 22452 | + return arUsage(stderr); | |
| 22453 | + } | |
| 22060 | 22454 | return SQLITE_OK; |
| 22061 | 22455 | } |
| 22062 | 22456 | |
| 22063 | 22457 | /* |
| 22064 | 22458 | ** This function assumes that all arguments within the ArCommand.azArg[] |
| @@ -22820,11 +23214,11 @@ | ||
| 22820 | 23214 | rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0); |
| 22821 | 23215 | rc_err_oom_die(rc); |
| 22822 | 23216 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 22823 | 23217 | rc = sqlite3_step(pStmt); |
| 22824 | 23218 | sqlite3_finalize(pStmt); |
| 22825 | - assert(rc==SQLITE_DONE); | |
| 23219 | + if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); | |
| 22826 | 23220 | } |
| 22827 | 23221 | assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ |
| 22828 | 23222 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 22829 | 23223 | rc_err_oom_die(rc); |
| 22830 | 23224 | rc = sqlite3_step(pStmt); |
| @@ -23752,12 +24146,12 @@ | ||
| 23752 | 24146 | if( nSep>1 ){ |
| 23753 | 24147 | raw_printf(stderr, "Error: multi-character row separators not allowed" |
| 23754 | 24148 | " for import\n"); |
| 23755 | 24149 | goto meta_command_exit; |
| 23756 | 24150 | } |
| 23757 | - sCtx.cColSep = p->colSeparator[0]; | |
| 23758 | - sCtx.cRowSep = p->rowSeparator[0]; | |
| 24151 | + sCtx.cColSep = (u8)p->colSeparator[0]; | |
| 24152 | + sCtx.cRowSep = (u8)p->rowSeparator[0]; | |
| 23759 | 24153 | } |
| 23760 | 24154 | sCtx.zFile = zFile; |
| 23761 | 24155 | sCtx.nLine = 1; |
| 23762 | 24156 | if( sCtx.zFile[0]=='|' ){ |
| 23763 | 24157 | #ifdef SQLITE_OMIT_POPEN |
| @@ -23949,10 +24343,16 @@ | ||
| 23949 | 24343 | sqlite3_stmt *pStmt; |
| 23950 | 24344 | int tnum = 0; |
| 23951 | 24345 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 23952 | 24346 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 23953 | 24347 | int i; |
| 24348 | + if( !ShellHasFlag(p,SHFLG_TestingMode) ){ | |
| 24349 | + utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n", | |
| 24350 | + "imposter"); | |
| 24351 | + rc = 1; | |
| 24352 | + goto meta_command_exit; | |
| 24353 | + } | |
| 23954 | 24354 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| 23955 | 24355 | utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" |
| 23956 | 24356 | " .imposter off\n"); |
| 23957 | 24357 | /* Also allowed, but not documented: |
| 23958 | 24358 | ** |
| @@ -24133,11 +24533,12 @@ | ||
| 24133 | 24533 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE) |
| 24134 | 24534 | if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){ |
| 24135 | 24535 | const char *zFile, *zProc; |
| 24136 | 24536 | char *zErrMsg = 0; |
| 24137 | 24537 | failIfSafeMode(p, "cannot run .load in safe mode"); |
| 24138 | - if( nArg<2 ){ | |
| 24538 | + if( nArg<2 || azArg[1][0]==0 ){ | |
| 24539 | + /* Must have a non-empty FILE. (Will not load self.) */ | |
| 24139 | 24540 | raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); |
| 24140 | 24541 | rc = 1; |
| 24141 | 24542 | goto meta_command_exit; |
| 24142 | 24543 | } |
| 24143 | 24544 | zFile = azArg[1]; |
| @@ -25457,32 +25858,40 @@ | ||
| 25457 | 25858 | "' OR ') as query, tname from tabcols group by tname)" |
| 25458 | 25859 | , zRevText); |
| 25459 | 25860 | shell_check_oom(zRevText); |
| 25460 | 25861 | if( bDebug ) utf8_printf(p->out, "%s\n", zRevText); |
| 25461 | 25862 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 25462 | - assert(lrc==SQLITE_OK); | |
| 25463 | - if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC); | |
| 25464 | - lrc = SQLITE_ROW==sqlite3_step(pStmt); | |
| 25465 | - if( lrc ){ | |
| 25466 | - const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); | |
| 25467 | - sqlite3_stmt *pCheckStmt; | |
| 25468 | - lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); | |
| 25469 | - if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery); | |
| 25470 | - if( SQLITE_OK==lrc ){ | |
| 25471 | - if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ | |
| 25472 | - double countIrreversible = sqlite3_column_double(pCheckStmt, 0); | |
| 25473 | - if( countIrreversible>0 ){ | |
| 25474 | - int sz = (int)(countIrreversible + 0.5); | |
| 25475 | - utf8_printf(stderr, | |
| 25476 | - "Digest includes %d invalidly encoded text field%s.\n", | |
| 25477 | - sz, (sz>1)? "s": ""); | |
| 25478 | - } | |
| 25479 | - } | |
| 25480 | - sqlite3_finalize(pCheckStmt); | |
| 25481 | - } | |
| 25482 | - sqlite3_finalize(pStmt); | |
| 25483 | - } | |
| 25863 | + if( lrc!=SQLITE_OK ){ | |
| 25864 | + /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the | |
| 25865 | + ** user does cruel and unnatural things like ".limit expr_depth 0". */ | |
| 25866 | + rc = 1; | |
| 25867 | + }else{ | |
| 25868 | + if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC); | |
| 25869 | + lrc = SQLITE_ROW==sqlite3_step(pStmt); | |
| 25870 | + if( lrc ){ | |
| 25871 | + const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); | |
| 25872 | + sqlite3_stmt *pCheckStmt; | |
| 25873 | + lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); | |
| 25874 | + if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery); | |
| 25875 | + if( lrc!=SQLITE_OK ){ | |
| 25876 | + rc = 1; | |
| 25877 | + }else{ | |
| 25878 | + if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ | |
| 25879 | + double countIrreversible = sqlite3_column_double(pCheckStmt, 0); | |
| 25880 | + if( countIrreversible>0 ){ | |
| 25881 | + int sz = (int)(countIrreversible + 0.5); | |
| 25882 | + utf8_printf(stderr, | |
| 25883 | + "Digest includes %d invalidly encoded text field%s.\n", | |
| 25884 | + sz, (sz>1)? "s": ""); | |
| 25885 | + } | |
| 25886 | + } | |
| 25887 | + sqlite3_finalize(pCheckStmt); | |
| 25888 | + } | |
| 25889 | + sqlite3_finalize(pStmt); | |
| 25890 | + } | |
| 25891 | + } | |
| 25892 | + if( rc ) utf8_printf(stderr, ".sha3sum failed.\n"); | |
| 25484 | 25893 | sqlite3_free(zRevText); |
| 25485 | 25894 | } |
| 25486 | 25895 | #endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */ |
| 25487 | 25896 | sqlite3_free(zSql); |
| 25488 | 25897 | }else |
| @@ -25741,10 +26150,16 @@ | ||
| 25741 | 26150 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 25742 | 26151 | int isOk = 0; |
| 25743 | 26152 | int i, n2; |
| 25744 | 26153 | const char *zCmd = 0; |
| 25745 | 26154 | |
| 26155 | + if( !ShellHasFlag(p,SHFLG_TestingMode) ){ | |
| 26156 | + utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n", | |
| 26157 | + "testctrl"); | |
| 26158 | + rc = 1; | |
| 26159 | + goto meta_command_exit; | |
| 26160 | + } | |
| 25746 | 26161 | open_db(p, 0); |
| 25747 | 26162 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 25748 | 26163 | |
| 25749 | 26164 | /* The argument can optionally begin with "-" or "--" */ |
| 25750 | 26165 | if( zCmd[0]=='-' && zCmd[1] ){ |
| @@ -26740,10 +27155,14 @@ | ||
| 26740 | 27155 | " -sorterref SIZE sorter references threshold size\n" |
| 26741 | 27156 | #endif |
| 26742 | 27157 | " -stats print memory stats before each finalize\n" |
| 26743 | 27158 | " -table set output mode to 'table'\n" |
| 26744 | 27159 | " -tabs set output mode to 'tabs'\n" |
| 27160 | + " -unsafe-testing allow unsafe commands and modes for testing\n" | |
| 27161 | +#if SHELL_WIN_UTF8_OPT | |
| 27162 | + " -utf8 setup interactive console code page for UTF-8\n" | |
| 27163 | +#endif | |
| 26745 | 27164 | " -version show SQLite version\n" |
| 26746 | 27165 | " -vfs NAME use NAME as the default VFS\n" |
| 26747 | 27166 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 26748 | 27167 | " -vfstrace enable tracing of all VFS calls\n" |
| 26749 | 27168 | #endif |
| @@ -26831,10 +27250,14 @@ | ||
| 26831 | 27250 | argv[0], argv[argc-1]); |
| 26832 | 27251 | exit(1); |
| 26833 | 27252 | } |
| 26834 | 27253 | return argv[i]; |
| 26835 | 27254 | } |
| 27255 | + | |
| 27256 | +static void sayAbnormalExit(void){ | |
| 27257 | + if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n"); | |
| 27258 | +} | |
| 26836 | 27259 | |
| 26837 | 27260 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 26838 | 27261 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 26839 | 27262 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 26840 | 27263 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -26852,11 +27275,11 @@ | ||
| 26852 | 27275 | #else |
| 26853 | 27276 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 26854 | 27277 | char **argv; |
| 26855 | 27278 | #endif |
| 26856 | 27279 | #ifdef SQLITE_DEBUG |
| 26857 | - sqlite3_int64 mem_main_enter = sqlite3_memory_used(); | |
| 27280 | + sqlite3_int64 mem_main_enter = 0; | |
| 26858 | 27281 | #endif |
| 26859 | 27282 | char *zErrMsg = 0; |
| 26860 | 27283 | #ifdef SQLITE_SHELL_FIDDLE |
| 26861 | 27284 | # define data shellState |
| 26862 | 27285 | #else |
| @@ -26873,22 +27296,27 @@ | ||
| 26873 | 27296 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 26874 | 27297 | #if !SQLITE_SHELL_IS_UTF8 |
| 26875 | 27298 | char **argvToFree = 0; |
| 26876 | 27299 | int argcToFree = 0; |
| 26877 | 27300 | #endif |
| 26878 | - | |
| 26879 | - setBinaryMode(stdin, 0); | |
| 26880 | 27301 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 27302 | + | |
| 26881 | 27303 | #ifdef SQLITE_SHELL_FIDDLE |
| 26882 | 27304 | stdin_is_interactive = 0; |
| 26883 | 27305 | stdout_is_console = 1; |
| 26884 | 27306 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 26885 | 27307 | #else |
| 26886 | 27308 | stdin_is_interactive = isatty(0); |
| 26887 | 27309 | stdout_is_console = isatty(1); |
| 26888 | 27310 | #endif |
| 26889 | - | |
| 27311 | +#if SHELL_WIN_UTF8_OPT | |
| 27312 | + atexit(console_restore); /* Needs revision for CLI as library call */ | |
| 27313 | +#endif | |
| 27314 | + atexit(sayAbnormalExit); | |
| 27315 | +#ifdef SQLITE_DEBUG | |
| 27316 | + mem_main_enter = sqlite3_memory_used(); | |
| 27317 | +#endif | |
| 26890 | 27318 | #if !defined(_WIN32_WCE) |
| 26891 | 27319 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 26892 | 27320 | if( isatty(0) && isatty(2) ){ |
| 26893 | 27321 | fprintf(stderr, |
| 26894 | 27322 | "attach debugger to process %d and press any key to continue.\n", |
| @@ -26904,10 +27332,18 @@ | ||
| 26904 | 27332 | #elif defined(SIGTRAP) |
| 26905 | 27333 | raise(SIGTRAP); |
| 26906 | 27334 | #endif |
| 26907 | 27335 | } |
| 26908 | 27336 | } |
| 27337 | +#endif | |
| 27338 | + /* Register a valid signal handler early, before much else is done. */ | |
| 27339 | +#ifdef SIGINT | |
| 27340 | + signal(SIGINT, interrupt_handler); | |
| 27341 | +#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) | |
| 27342 | + if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ | |
| 27343 | + fprintf(stderr, "No ^C handler.\n"); | |
| 27344 | + } | |
| 26909 | 27345 | #endif |
| 26910 | 27346 | |
| 26911 | 27347 | #if USE_SYSTEM_SQLITE+0!=1 |
| 26912 | 27348 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 26913 | 27349 | utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| @@ -26944,19 +27380,10 @@ | ||
| 26944 | 27380 | #endif |
| 26945 | 27381 | |
| 26946 | 27382 | assert( argc>=1 && argv && argv[0] ); |
| 26947 | 27383 | Argv0 = argv[0]; |
| 26948 | 27384 | |
| 26949 | - /* Make sure we have a valid signal handler early, before anything | |
| 26950 | - ** else is done. | |
| 26951 | - */ | |
| 26952 | -#ifdef SIGINT | |
| 26953 | - signal(SIGINT, interrupt_handler); | |
| 26954 | -#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) | |
| 26955 | - SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); | |
| 26956 | -#endif | |
| 26957 | - | |
| 26958 | 27385 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 26959 | 27386 | { |
| 26960 | 27387 | /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name |
| 26961 | 27388 | ** of a C-function that will provide the name of the database file. Use |
| 26962 | 27389 | ** this compile-time option to embed this shell program in larger |
| @@ -27109,10 +27536,12 @@ | ||
| 27109 | 27536 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 27110 | 27537 | bail_on_error = 1; |
| 27111 | 27538 | }else if( cli_strcmp(z,"-nonce")==0 ){ |
| 27112 | 27539 | free(data.zNonce); |
| 27113 | 27540 | data.zNonce = strdup(argv[++i]); |
| 27541 | + }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ | |
| 27542 | + ShellSetFlag(&data,SHFLG_TestingMode); | |
| 27114 | 27543 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27115 | 27544 | /* no-op - catch this on the second pass */ |
| 27116 | 27545 | } |
| 27117 | 27546 | } |
| 27118 | 27547 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -27271,10 +27700,14 @@ | ||
| 27271 | 27700 | return 0; |
| 27272 | 27701 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 27273 | 27702 | stdin_is_interactive = 1; |
| 27274 | 27703 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 27275 | 27704 | stdin_is_interactive = 0; |
| 27705 | + }else if( cli_strcmp(z,"-utf8")==0 ){ | |
| 27706 | +#if SHELL_WIN_UTF8_OPT | |
| 27707 | + console_utf8 = 1; | |
| 27708 | +#endif /* SHELL_WIN_UTF8_OPT */ | |
| 27276 | 27709 | }else if( cli_strcmp(z,"-heap")==0 ){ |
| 27277 | 27710 | i++; |
| 27278 | 27711 | }else if( cli_strcmp(z,"-pagecache")==0 ){ |
| 27279 | 27712 | i+=2; |
| 27280 | 27713 | }else if( cli_strcmp(z,"-lookaside")==0 ){ |
| @@ -27341,17 +27774,27 @@ | ||
| 27341 | 27774 | readStdin = 0; |
| 27342 | 27775 | break; |
| 27343 | 27776 | #endif |
| 27344 | 27777 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27345 | 27778 | data.bSafeMode = data.bSafeModePersist = 1; |
| 27779 | + }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ | |
| 27780 | + /* Acted upon in first pass. */ | |
| 27346 | 27781 | }else{ |
| 27347 | 27782 | utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 27348 | 27783 | raw_printf(stderr,"Use -help for a list of options.\n"); |
| 27349 | 27784 | return 1; |
| 27350 | 27785 | } |
| 27351 | 27786 | data.cMode = data.mode; |
| 27352 | 27787 | } |
| 27788 | +#if SHELL_WIN_UTF8_OPT | |
| 27789 | + if( console_utf8 && stdin_is_interactive ){ | |
| 27790 | + console_prepare(); | |
| 27791 | + }else{ | |
| 27792 | + setBinaryMode(stdin, 0); | |
| 27793 | + console_utf8 = 0; | |
| 27794 | + } | |
| 27795 | +#endif | |
| 27353 | 27796 | |
| 27354 | 27797 | if( !readStdin ){ |
| 27355 | 27798 | /* Run all arguments that do not begin with '-' as if they were separate |
| 27356 | 27799 | ** command-line inputs, except for the argToSkip argument which contains |
| 27357 | 27800 | ** the database filename. |
| 27358 | 27801 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -244,10 +244,11 @@ | |
| 244 | |
| 245 | #if defined(_WIN32) || defined(WIN32) |
| 246 | #if SQLITE_OS_WINRT |
| 247 | #include <intrin.h> |
| 248 | #endif |
| 249 | #include <windows.h> |
| 250 | |
| 251 | /* string conversion routines only needed on Win32 */ |
| 252 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 253 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -458,14 +459,28 @@ | |
| 458 | ** at an error if we are not interactive. |
| 459 | */ |
| 460 | static int bail_on_error = 0; |
| 461 | |
| 462 | /* |
| 463 | ** Threat stdin as an interactive input if the following variable |
| 464 | ** is true. Otherwise, assume stdin is connected to a file or pipe. |
| 465 | */ |
| 466 | static int stdin_is_interactive = 1; |
| 467 | |
| 468 | /* |
| 469 | ** On Windows systems we have to know if standard output is a console |
| 470 | ** in order to translate UTF-8 into MBCS. The following variable is |
| 471 | ** true if translation is required. |
| @@ -595,20 +610,154 @@ | |
| 595 | } |
| 596 | return dynPrompt.dynamicPrompt; |
| 597 | } |
| 598 | #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ |
| 599 | |
| 600 | /* |
| 601 | ** Render output like fprintf(). Except, if the output is going to the |
| 602 | ** console and if this is running on a Windows machine, translate the |
| 603 | ** output from UTF-8 into MBCS. |
| 604 | */ |
| 605 | #if defined(_WIN32) || defined(WIN32) |
| 606 | void utf8_printf(FILE *out, const char *zFormat, ...){ |
| 607 | va_list ap; |
| 608 | va_start(ap, zFormat); |
| 609 | if( stdout_is_console && (out==stdout || out==stderr) ){ |
| 610 | char *z1 = sqlite3_vmprintf(zFormat, ap); |
| 611 | char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); |
| 612 | sqlite3_free(z1); |
| 613 | fputs(z2, out); |
| 614 | sqlite3_free(z2); |
| @@ -636,11 +785,11 @@ | |
| 636 | } |
| 637 | |
| 638 | /* Check a pointer to see if it is NULL. If it is NULL, exit with an |
| 639 | ** out-of-memory error. |
| 640 | */ |
| 641 | static void shell_check_oom(void *p){ |
| 642 | if( p==0 ) shell_out_of_memory(); |
| 643 | } |
| 644 | |
| 645 | /* |
| 646 | ** Write I/O traces to the following stream. |
| @@ -815,13 +964,18 @@ | |
| 815 | zLine[n] = 0; |
| 816 | break; |
| 817 | } |
| 818 | } |
| 819 | #if defined(_WIN32) || defined(WIN32) |
| 820 | /* For interactive input on Windows systems, translate the |
| 821 | ** multi-byte characterset characters into UTF-8. */ |
| 822 | if( stdin_is_interactive && in==stdin ){ |
| 823 | char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); |
| 824 | if( zTrans ){ |
| 825 | i64 nTrans = strlen(zTrans)+1; |
| 826 | if( nTrans>nLine ){ |
| 827 | zLine = realloc(zLine, nTrans); |
| @@ -858,14 +1012,25 @@ | |
| 858 | }else{ |
| 859 | zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt; |
| 860 | #if SHELL_USE_LOCAL_GETLINE |
| 861 | printf("%s", zPrompt); |
| 862 | fflush(stdout); |
| 863 | zResult = local_getline(zPrior, stdin); |
| 864 | #else |
| 865 | free(zPrior); |
| 866 | zResult = shell_readline(zPrompt); |
| 867 | if( zResult && *zResult ) shell_add_history(zResult); |
| 868 | #endif |
| 869 | } |
| 870 | return zResult; |
| 871 | } |
| @@ -1001,10 +1166,11 @@ | |
| 1001 | ** |
| 1002 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1003 | */ |
| 1004 | static char quoteChar(const char *zName){ |
| 1005 | int i; |
| 1006 | if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; |
| 1007 | for(i=0; zName[i]; i++){ |
| 1008 | if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; |
| 1009 | } |
| 1010 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| @@ -1644,11 +1810,12 @@ | |
| 1644 | ** May you find forgiveness for yourself and forgive others. |
| 1645 | ** May you share freely, never taking more than you give. |
| 1646 | ** |
| 1647 | ****************************************************************************** |
| 1648 | ** |
| 1649 | ** This SQLite extension implements functions that compute SHA3 hashes. |
| 1650 | ** Two SQL functions are implemented: |
| 1651 | ** |
| 1652 | ** sha3(X,SIZE) |
| 1653 | ** sha3_query(Y,SIZE) |
| 1654 | ** |
| @@ -3244,22 +3411,22 @@ | |
| 3244 | *pOut = 0; |
| 3245 | return pOut; |
| 3246 | } |
| 3247 | |
| 3248 | /* Skip over text which is not base64 numeral(s). */ |
| 3249 | static char * skipNonB64( char *s ){ |
| 3250 | char c; |
| 3251 | while( (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s; |
| 3252 | return s; |
| 3253 | } |
| 3254 | |
| 3255 | /* Decode base64 text into a byte buffer. */ |
| 3256 | static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){ |
| 3257 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3258 | while( ncIn>0 && *pIn!=PAD_CHAR ){ |
| 3259 | static signed char nboi[] = { 0, 0, 1, 2, 3 }; |
| 3260 | char *pUse = skipNonB64(pIn); |
| 3261 | unsigned long qv = 0L; |
| 3262 | int nti, nbo, nac; |
| 3263 | ncIn -= (pUse - pIn); |
| 3264 | pIn = pUse; |
| 3265 | nti = (ncIn>4)? 4 : ncIn; |
| @@ -3314,14 +3481,21 @@ | |
| 3314 | nc = 4*(nv+2/3); /* quads needed */ |
| 3315 | nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */ |
| 3316 | if( nvMax < nc ){ |
| 3317 | sqlite3_result_error(context, "blob expanded to base64 too big", -1); |
| 3318 | return; |
| 3319 | } |
| 3320 | cBuf = sqlite3_malloc(nc); |
| 3321 | if( !cBuf ) goto memFail; |
| 3322 | bBuf = (u8*)sqlite3_value_blob(av[0]); |
| 3323 | nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf); |
| 3324 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3325 | break; |
| 3326 | case SQLITE_TEXT: |
| 3327 | nc = nv; |
| @@ -3329,14 +3503,21 @@ | |
| 3329 | if( nvMax < nb ){ |
| 3330 | sqlite3_result_error(context, "blob from base64 may be too big", -1); |
| 3331 | return; |
| 3332 | }else if( nb<1 ){ |
| 3333 | nb = 1; |
| 3334 | } |
| 3335 | bBuf = sqlite3_malloc(nb); |
| 3336 | if( !bBuf ) goto memFail; |
| 3337 | cBuf = (char *)sqlite3_value_text(av[0]); |
| 3338 | nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf); |
| 3339 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3340 | break; |
| 3341 | default: |
| 3342 | sqlite3_result_error(context, "base64 accepts only blob or text", -1); |
| @@ -3520,13 +3701,13 @@ | |
| 3520 | |
| 3521 | /* Width of base64 lines. Should be an integer multiple of 5. */ |
| 3522 | #define B85_DARK_MAX 80 |
| 3523 | |
| 3524 | |
| 3525 | static char * skipNonB85( char *s ){ |
| 3526 | char c; |
| 3527 | while( (c = *s) && !IS_B85(c) ) ++s; |
| 3528 | return s; |
| 3529 | } |
| 3530 | |
| 3531 | /* Convert small integer, known to be in 0..84 inclusive, to base85 numeral. |
| 3532 | * Do not use the macro form with argument expression having a side-effect.*/ |
| @@ -3592,11 +3773,11 @@ | |
| 3592 | /* Decode base85 text into a byte buffer. */ |
| 3593 | static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){ |
| 3594 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3595 | while( ncIn>0 ){ |
| 3596 | static signed char nboi[] = { 0, 0, 1, 2, 3, 4 }; |
| 3597 | char *pUse = skipNonB85(pIn); |
| 3598 | unsigned long qv = 0L; |
| 3599 | int nti, nbo; |
| 3600 | ncIn -= (pUse - pIn); |
| 3601 | pIn = pUse; |
| 3602 | nti = (ncIn>5)? 5 : ncIn; |
| @@ -3676,14 +3857,21 @@ | |
| 3676 | /* ulongs tail newlines tailenc+nul*/ |
| 3677 | nc = 5*(nv/4) + nv%4 + nv/64+1 + 2; |
| 3678 | if( nvMax < nc ){ |
| 3679 | sqlite3_result_error(context, "blob expanded to base85 too big", -1); |
| 3680 | return; |
| 3681 | } |
| 3682 | cBuf = sqlite3_malloc(nc); |
| 3683 | if( !cBuf ) goto memFail; |
| 3684 | bBuf = (u8*)sqlite3_value_blob(av[0]); |
| 3685 | nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf); |
| 3686 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3687 | break; |
| 3688 | case SQLITE_TEXT: |
| 3689 | nc = nv; |
| @@ -3691,14 +3879,21 @@ | |
| 3691 | if( nvMax < nb ){ |
| 3692 | sqlite3_result_error(context, "blob from base85 may be too big", -1); |
| 3693 | return; |
| 3694 | }else if( nb<1 ){ |
| 3695 | nb = 1; |
| 3696 | } |
| 3697 | bBuf = sqlite3_malloc(nb); |
| 3698 | if( !bBuf ) goto memFail; |
| 3699 | cBuf = (char *)sqlite3_value_text(av[0]); |
| 3700 | nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf); |
| 3701 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3702 | break; |
| 3703 | default: |
| 3704 | sqlite3_result_error(context, "base85 accepts only blob or text.", -1); |
| @@ -4114,11 +4309,11 @@ | |
| 4114 | } |
| 4115 | |
| 4116 | /************************* End ../ext/misc/ieee754.c ********************/ |
| 4117 | /************************* Begin ../ext/misc/series.c ******************/ |
| 4118 | /* |
| 4119 | ** 2015-08-18 |
| 4120 | ** |
| 4121 | ** The author disclaims copyright to this source code. In place of |
| 4122 | ** a legal notice, here is a blessing: |
| 4123 | ** |
| 4124 | ** May you do good and not evil. |
| @@ -4127,11 +4322,23 @@ | |
| 4127 | ** |
| 4128 | ************************************************************************* |
| 4129 | ** |
| 4130 | ** This file demonstrates how to create a table-valued-function using |
| 4131 | ** a virtual table. This demo implements the generate_series() function |
| 4132 | ** which gives similar results to the eponymous function in PostgreSQL. |
| 4133 | ** Examples: |
| 4134 | ** |
| 4135 | ** SELECT * FROM generate_series(0,100,5); |
| 4136 | ** |
| 4137 | ** The query above returns integers from 0 through 100 counting by steps |
| @@ -4143,10 +4350,18 @@ | |
| 4143 | ** |
| 4144 | ** SELECT * FROM generate_series(20) LIMIT 10; |
| 4145 | ** |
| 4146 | ** Integers 20 through 29. |
| 4147 | ** |
| 4148 | ** HOW IT WORKS |
| 4149 | ** |
| 4150 | ** The generate_series "function" is really a virtual table with the |
| 4151 | ** following schema: |
| 4152 | ** |
| @@ -4154,10 +4369,13 @@ | |
| 4154 | ** value, |
| 4155 | ** start HIDDEN, |
| 4156 | ** stop HIDDEN, |
| 4157 | ** step HIDDEN |
| 4158 | ** ); |
| 4159 | ** |
| 4160 | ** Function arguments in queries against this virtual table are translated |
| 4161 | ** into equality constraints against successive hidden columns. In other |
| 4162 | ** words, the following pairs of queries are equivalent to each other: |
| 4163 | ** |
| @@ -4187,27 +4405,139 @@ | |
| 4187 | */ |
| 4188 | /* #include "sqlite3ext.h" */ |
| 4189 | SQLITE_EXTENSION_INIT1 |
| 4190 | #include <assert.h> |
| 4191 | #include <string.h> |
| 4192 | |
| 4193 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 4194 | |
| 4195 | |
| 4196 | /* series_cursor is a subclass of sqlite3_vtab_cursor which will |
| 4197 | ** serve as the underlying representation of a cursor that scans |
| 4198 | ** over rows of the result |
| 4199 | */ |
| 4200 | typedef struct series_cursor series_cursor; |
| 4201 | struct series_cursor { |
| 4202 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 4203 | int isDesc; /* True to count down rather than up */ |
| 4204 | sqlite3_int64 iRowid; /* The rowid */ |
| 4205 | sqlite3_int64 iValue; /* Current value ("value") */ |
| 4206 | sqlite3_int64 mnValue; /* Mimimum value ("start") */ |
| 4207 | sqlite3_int64 mxValue; /* Maximum value ("stop") */ |
| 4208 | sqlite3_int64 iStep; /* Increment ("step") */ |
| 4209 | }; |
| 4210 | |
| 4211 | /* |
| 4212 | ** The seriesConnect() method is invoked to create a new |
| 4213 | ** series_vtab that describes the generate_series virtual table. |
| @@ -4285,16 +4615,11 @@ | |
| 4285 | /* |
| 4286 | ** Advance a series_cursor to its next row of output. |
| 4287 | */ |
| 4288 | static int seriesNext(sqlite3_vtab_cursor *cur){ |
| 4289 | series_cursor *pCur = (series_cursor*)cur; |
| 4290 | if( pCur->isDesc ){ |
| 4291 | pCur->iValue -= pCur->iStep; |
| 4292 | }else{ |
| 4293 | pCur->iValue += pCur->iStep; |
| 4294 | } |
| 4295 | pCur->iRowid++; |
| 4296 | return SQLITE_OK; |
| 4297 | } |
| 4298 | |
| 4299 | /* |
| 4300 | ** Return values of columns for the row at which the series_cursor |
| @@ -4306,44 +4631,40 @@ | |
| 4306 | int i /* Which column to return */ |
| 4307 | ){ |
| 4308 | series_cursor *pCur = (series_cursor*)cur; |
| 4309 | sqlite3_int64 x = 0; |
| 4310 | switch( i ){ |
| 4311 | case SERIES_COLUMN_START: x = pCur->mnValue; break; |
| 4312 | case SERIES_COLUMN_STOP: x = pCur->mxValue; break; |
| 4313 | case SERIES_COLUMN_STEP: x = pCur->iStep; break; |
| 4314 | default: x = pCur->iValue; break; |
| 4315 | } |
| 4316 | sqlite3_result_int64(ctx, x); |
| 4317 | return SQLITE_OK; |
| 4318 | } |
| 4319 | |
| 4320 | /* |
| 4321 | ** Return the rowid for the current row. In this implementation, the |
| 4322 | ** first row returned is assigned rowid value 1, and each subsequent |
| 4323 | ** row a value 1 more than that of the previous. |
| 4324 | */ |
| 4325 | static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| 4326 | series_cursor *pCur = (series_cursor*)cur; |
| 4327 | *pRowid = pCur->iRowid; |
| 4328 | return SQLITE_OK; |
| 4329 | } |
| 4330 | |
| 4331 | /* |
| 4332 | ** Return TRUE if the cursor has been moved off of the last |
| 4333 | ** row of output. |
| 4334 | */ |
| 4335 | static int seriesEof(sqlite3_vtab_cursor *cur){ |
| 4336 | series_cursor *pCur = (series_cursor*)cur; |
| 4337 | if( pCur->isDesc ){ |
| 4338 | return pCur->iValue < pCur->mnValue; |
| 4339 | }else{ |
| 4340 | return pCur->iValue > pCur->mxValue; |
| 4341 | } |
| 4342 | } |
| 4343 | |
| 4344 | /* True to cause run-time checking of the start=, stop=, and/or step= |
| 4345 | ** parameters. The only reason to do this is for testing the |
| 4346 | ** constraint checking logic for virtual tables in the SQLite core. |
| 4347 | */ |
| 4348 | #ifndef SQLITE_SERIES_CONSTRAINT_VERIFY |
| 4349 | # define SQLITE_SERIES_CONSTRAINT_VERIFY 0 |
| @@ -4350,11 +4671,11 @@ | |
| 4350 | #endif |
| 4351 | |
| 4352 | /* |
| 4353 | ** This method is called to "rewind" the series_cursor object back |
| 4354 | ** to the first row of output. This method is always called at least |
| 4355 | ** once prior to any call to seriesColumn() or seriesRowid() or |
| 4356 | ** seriesEof(). |
| 4357 | ** |
| 4358 | ** The query plan selected by seriesBestIndex is passed in the idxNum |
| 4359 | ** parameter. (idxStr is not used in this implementation.) idxNum |
| 4360 | ** is a bitmask showing which constraints are available: |
| @@ -4370,58 +4691,53 @@ | |
| 4370 | ** This routine should initialize the cursor and position it so that it |
| 4371 | ** is pointing at the first row, or pointing off the end of the table |
| 4372 | ** (so that seriesEof() will return true) if the table is empty. |
| 4373 | */ |
| 4374 | static int seriesFilter( |
| 4375 | sqlite3_vtab_cursor *pVtabCursor, |
| 4376 | int idxNum, const char *idxStrUnused, |
| 4377 | int argc, sqlite3_value **argv |
| 4378 | ){ |
| 4379 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 4380 | int i = 0; |
| 4381 | (void)idxStrUnused; |
| 4382 | if( idxNum & 1 ){ |
| 4383 | pCur->mnValue = sqlite3_value_int64(argv[i++]); |
| 4384 | }else{ |
| 4385 | pCur->mnValue = 0; |
| 4386 | } |
| 4387 | if( idxNum & 2 ){ |
| 4388 | pCur->mxValue = sqlite3_value_int64(argv[i++]); |
| 4389 | }else{ |
| 4390 | pCur->mxValue = 0xffffffff; |
| 4391 | } |
| 4392 | if( idxNum & 4 ){ |
| 4393 | pCur->iStep = sqlite3_value_int64(argv[i++]); |
| 4394 | if( pCur->iStep==0 ){ |
| 4395 | pCur->iStep = 1; |
| 4396 | }else if( pCur->iStep<0 ){ |
| 4397 | pCur->iStep = -pCur->iStep; |
| 4398 | if( (idxNum & 16)==0 ) idxNum |= 8; |
| 4399 | } |
| 4400 | }else{ |
| 4401 | pCur->iStep = 1; |
| 4402 | } |
| 4403 | for(i=0; i<argc; i++){ |
| 4404 | if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ |
| 4405 | /* If any of the constraints have a NULL value, then return no rows. |
| 4406 | ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ |
| 4407 | pCur->mnValue = 1; |
| 4408 | pCur->mxValue = 0; |
| 4409 | break; |
| 4410 | } |
| 4411 | } |
| 4412 | if( idxNum & 8 ){ |
| 4413 | pCur->isDesc = 1; |
| 4414 | pCur->iValue = pCur->mxValue; |
| 4415 | if( pCur->iStep>0 ){ |
| 4416 | pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep; |
| 4417 | } |
| 4418 | }else{ |
| 4419 | pCur->isDesc = 0; |
| 4420 | pCur->iValue = pCur->mnValue; |
| 4421 | } |
| 4422 | pCur->iRowid = 1; |
| 4423 | return SQLITE_OK; |
| 4424 | } |
| 4425 | |
| 4426 | /* |
| 4427 | ** SQLite will invoke this method one or more times while planning a query |
| @@ -5172,11 +5488,11 @@ | |
| 5172 | re_append(p, RE_OP_FORK, -sz); |
| 5173 | } |
| 5174 | break; |
| 5175 | } |
| 5176 | case '[': { |
| 5177 | int iFirst = p->nState; |
| 5178 | if( rePeek(p)=='^' ){ |
| 5179 | re_append(p, RE_OP_CC_EXC, 0); |
| 5180 | p->sIn.i++; |
| 5181 | }else{ |
| 5182 | re_append(p, RE_OP_CC_INC, 0); |
| @@ -5196,11 +5512,11 @@ | |
| 5196 | re_append(p, RE_OP_CC_VALUE, c); |
| 5197 | } |
| 5198 | if( rePeek(p)==']' ){ p->sIn.i++; break; } |
| 5199 | } |
| 5200 | if( c==0 ) return "unclosed '['"; |
| 5201 | p->aArg[iFirst] = p->nState - iFirst; |
| 5202 | break; |
| 5203 | } |
| 5204 | case '\\': { |
| 5205 | int specialOp = 0; |
| 5206 | switch( rePeek(p) ){ |
| @@ -9213,13 +9529,23 @@ | |
| 9213 | ** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry |
| 9214 | ** linked list. Remove it from the list and free the object. |
| 9215 | */ |
| 9216 | static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ |
| 9217 | if( pOld ){ |
| 9218 | ZipfileEntry **pp; |
| 9219 | for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); |
| 9220 | *pp = (*pp)->pNext; |
| 9221 | zipfileEntryFree(pOld); |
| 9222 | } |
| 9223 | } |
| 9224 | |
| 9225 | /* |
| @@ -12510,10 +12836,11 @@ | |
| 12510 | #endif |
| 12511 | |
| 12512 | #endif /* ifndef _SQLITE_RECOVER_H */ |
| 12513 | |
| 12514 | /************************* End ../ext/recover/sqlite3recover.h ********************/ |
| 12515 | /************************* Begin ../ext/recover/dbdata.c ******************/ |
| 12516 | /* |
| 12517 | ** 2019-04-17 |
| 12518 | ** |
| 12519 | ** The author disclaims copyright to this source code. In place of |
| @@ -14576,11 +14903,11 @@ | |
| 14576 | pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); |
| 14577 | while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 14578 | int iField = sqlite3_column_int(pStmt, 0); |
| 14579 | int iCol = sqlite3_column_int(pStmt, 1); |
| 14580 | |
| 14581 | assert( iField<pNew->nCol && iCol<pNew->nCol ); |
| 14582 | pNew->aCol[iCol].iField = iField; |
| 14583 | |
| 14584 | pNew->bIntkey = 0; |
| 14585 | iPk = -2; |
| 14586 | } |
| @@ -16340,10 +16667,11 @@ | |
| 16340 | } |
| 16341 | |
| 16342 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 16343 | |
| 16344 | /************************* End ../ext/recover/sqlite3recover.c ********************/ |
| 16345 | #endif |
| 16346 | #ifdef SQLITE_SHELL_EXTSRC |
| 16347 | # include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC) |
| 16348 | #endif |
| 16349 | |
| @@ -16521,10 +16849,11 @@ | |
| 16521 | #define SHFLG_CountChanges 0x00000020 /* .changes setting */ |
| 16522 | #define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */ |
| 16523 | #define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ |
| 16524 | #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ |
| 16525 | #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ |
| 16526 | |
| 16527 | /* |
| 16528 | ** Macros for testing and setting shellFlgs |
| 16529 | */ |
| 16530 | #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) |
| @@ -16764,10 +17093,11 @@ | |
| 16764 | if( hasCRNL ){ |
| 16765 | /* If the original contains \r\n then do no conversions back to \n */ |
| 16766 | }else{ |
| 16767 | /* If the file did not originally contain \r\n then convert any new |
| 16768 | ** \r\n back into \n */ |
| 16769 | for(i=j=0; i<sz; i++){ |
| 16770 | if( p[i]=='\r' && p[i+1]=='\n' ) i++; |
| 16771 | p[j++] = p[i]; |
| 16772 | } |
| 16773 | sz = j; |
| @@ -16854,10 +17184,11 @@ | |
| 16854 | */ |
| 16855 | static void output_quoted_string(FILE *out, const char *z){ |
| 16856 | int i; |
| 16857 | char c; |
| 16858 | setBinaryMode(out, 1); |
| 16859 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 16860 | if( c==0 ){ |
| 16861 | utf8_printf(out,"'%s'",z); |
| 16862 | }else{ |
| 16863 | raw_printf(out, "'"); |
| @@ -16983,10 +17314,11 @@ | |
| 16983 | /* |
| 16984 | ** Output the given string as a quoted according to JSON quoting rules. |
| 16985 | */ |
| 16986 | static void output_json_string(FILE *out, const char *z, i64 n){ |
| 16987 | unsigned int c; |
| 16988 | if( n<0 ) n = strlen(z); |
| 16989 | fputc('"', out); |
| 16990 | while( n-- ){ |
| 16991 | c = *(z++); |
| 16992 | if( c=='\\' || c=='"' ){ |
| @@ -17107,12 +17439,11 @@ | |
| 17107 | /* |
| 17108 | ** This routine runs when the user presses Ctrl-C |
| 17109 | */ |
| 17110 | static void interrupt_handler(int NotUsed){ |
| 17111 | UNUSED_PARAMETER(NotUsed); |
| 17112 | seenInterrupt++; |
| 17113 | if( seenInterrupt>2 ) exit(1); |
| 17114 | if( globalDb ) sqlite3_interrupt(globalDb); |
| 17115 | } |
| 17116 | |
| 17117 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 17118 | /* |
| @@ -18559,14 +18890,18 @@ | |
| 18559 | zVar = zNum; |
| 18560 | } |
| 18561 | sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC); |
| 18562 | if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){ |
| 18563 | sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0)); |
| 18564 | }else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){ |
| 18565 | sqlite3_bind_double(pStmt, i, NAN); |
| 18566 | }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ |
| 18567 | sqlite3_bind_double(pStmt, i, INFINITY); |
| 18568 | }else{ |
| 18569 | sqlite3_bind_null(pStmt, i); |
| 18570 | } |
| 18571 | sqlite3_reset(pQ); |
| 18572 | } |
| @@ -18806,11 +19141,11 @@ | |
| 18806 | nAlloc = nColumn*4; |
| 18807 | if( nAlloc<=0 ) nAlloc = 1; |
| 18808 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 18809 | shell_check_oom(azData); |
| 18810 | azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 18811 | shell_check_oom((void*)azNextLine); |
| 18812 | memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
| 18813 | if( p->cmOpts.bQuote ){ |
| 18814 | azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 18815 | shell_check_oom(azQuoted); |
| 18816 | memset(azQuoted, 0, nColumn*sizeof(char*) ); |
| @@ -18836,10 +19171,11 @@ | |
| 18836 | if( wx==0 ){ |
| 18837 | wx = p->cmOpts.iWrap; |
| 18838 | } |
| 18839 | if( wx<0 ) wx = -wx; |
| 18840 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 18841 | azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); |
| 18842 | } |
| 18843 | do{ |
| 18844 | int useNextLine = bNextLine; |
| 18845 | bNextLine = 0; |
| @@ -19769,17 +20105,17 @@ | |
| 19769 | " from the \".mode\" output mode", |
| 19770 | " * If FILE begins with \"|\" then it is a command that generates the", |
| 19771 | " input text.", |
| 19772 | #endif |
| 19773 | #ifndef SQLITE_OMIT_TEST_CONTROL |
| 19774 | ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", |
| 19775 | #endif |
| 19776 | ".indexes ?TABLE? Show names of indexes", |
| 19777 | " If TABLE is specified, only show indexes for", |
| 19778 | " tables matching TABLE using the LIKE operator.", |
| 19779 | #ifdef SQLITE_ENABLE_IOTRACE |
| 19780 | ".iotrace FILE Enable I/O diagnostic logging to FILE", |
| 19781 | #endif |
| 19782 | ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", |
| 19783 | ".lint OPTIONS Report potential schema issues.", |
| 19784 | " Options:", |
| 19785 | " fkey-indexes Find missing foreign key indexes", |
| @@ -19884,11 +20220,11 @@ | |
| 19884 | ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", |
| 19885 | ".schema ?PATTERN? Show the CREATE statements matching PATTERN", |
| 19886 | " Options:", |
| 19887 | " --indent Try to pretty-print the schema", |
| 19888 | " --nosys Omit objects whose names start with \"sqlite_\"", |
| 19889 | ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", |
| 19890 | " Options:", |
| 19891 | " --init Create a new SELFTEST table", |
| 19892 | " -v Verbose output", |
| 19893 | ".separator COL ?ROW? Change the column and row separators", |
| 19894 | #if defined(SQLITE_ENABLE_SESSION) |
| @@ -19926,13 +20262,13 @@ | |
| 19926 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 19927 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 19928 | #endif |
| 19929 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 19930 | #ifndef SQLITE_SHELL_FIDDLE |
| 19931 | ".testcase NAME Begin redirecting output to 'testcase-out.txt'", |
| 19932 | #endif |
| 19933 | ".testctrl CMD ... Run various sqlite3_test_control() operations", |
| 19934 | " Run \".testctrl\" with no arguments for details", |
| 19935 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 19936 | ".timer on|off Turn SQL timer on or off", |
| 19937 | #ifndef SQLITE_OMIT_TRACE |
| 19938 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| @@ -19980,20 +20316,45 @@ | |
| 19980 | || zPattern[0]=='0' |
| 19981 | || cli_strcmp(zPattern,"-a")==0 |
| 19982 | || cli_strcmp(zPattern,"-all")==0 |
| 19983 | || cli_strcmp(zPattern,"--all")==0 |
| 19984 | ){ |
| 19985 | /* Show all commands, but only one line per command */ |
| 19986 | if( zPattern==0 ) zPattern = ""; |
| 19987 | for(i=0; i<ArraySize(azHelp); i++){ |
| 19988 | if( azHelp[i][0]=='.' || zPattern[0] ){ |
| 19989 | utf8_printf(out, "%s\n", azHelp[i]); |
| 19990 | n++; |
| 19991 | } |
| 19992 | } |
| 19993 | }else{ |
| 19994 | /* Look for commands that for which zPattern is an exact prefix */ |
| 19995 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 19996 | shell_check_oom(zPat); |
| 19997 | for(i=0; i<ArraySize(azHelp); i++){ |
| 19998 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 19999 | utf8_printf(out, "%s\n", azHelp[i]); |
| @@ -20002,28 +20363,32 @@ | |
| 20002 | } |
| 20003 | } |
| 20004 | sqlite3_free(zPat); |
| 20005 | if( n ){ |
| 20006 | if( n==1 ){ |
| 20007 | /* when zPattern is a prefix of exactly one command, then include the |
| 20008 | ** details of that command, which should begin at offset j */ |
| 20009 | while( j<ArraySize(azHelp)-1 && azHelp[j][0]!='.' ){ |
| 20010 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20011 | j++; |
| 20012 | } |
| 20013 | } |
| 20014 | return n; |
| 20015 | } |
| 20016 | /* Look for commands that contain zPattern anywhere. Show the complete |
| 20017 | ** text of all commands that match. */ |
| 20018 | zPat = sqlite3_mprintf("%%%s%%", zPattern); |
| 20019 | shell_check_oom(zPat); |
| 20020 | for(i=0; i<ArraySize(azHelp); i++){ |
| 20021 | if( azHelp[i][0]=='.' ) j = i; |
| 20022 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 20023 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20024 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]!='.' ){ |
| 20025 | j++; |
| 20026 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20027 | } |
| 20028 | i = j; |
| 20029 | n++; |
| @@ -20337,17 +20702,34 @@ | |
| 20337 | } |
| 20338 | globalDb = p->db; |
| 20339 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 20340 | utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 20341 | zDbFilename, sqlite3_errmsg(p->db)); |
| 20342 | if( openFlags & OPEN_DB_KEEPALIVE ){ |
| 20343 | sqlite3_open(":memory:", &p->db); |
| 20344 | return; |
| 20345 | } |
| 20346 | exit(1); |
| 20347 | } |
| 20348 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| 20349 | |
| 20350 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 20351 | sqlite3_enable_load_extension(p->db, 1); |
| 20352 | #endif |
| 20353 | sqlite3_shathree_init(p->db, 0, 0); |
| @@ -20356,13 +20738,10 @@ | |
| 20356 | sqlite3_base64_init(p->db, 0, 0); |
| 20357 | sqlite3_base85_init(p->db, 0, 0); |
| 20358 | sqlite3_regexp_init(p->db, 0, 0); |
| 20359 | sqlite3_ieee_init(p->db, 0, 0); |
| 20360 | sqlite3_series_init(p->db, 0, 0); |
| 20361 | #if SQLITE_SHELL_HAVE_RECOVER |
| 20362 | sqlite3_dbdata_init(p->db, 0, 0); |
| 20363 | #endif |
| 20364 | #ifndef SQLITE_SHELL_FIDDLE |
| 20365 | sqlite3_fileio_init(p->db, 0, 0); |
| 20366 | sqlite3_completion_init(p->db, 0, 0); |
| 20367 | #endif |
| 20368 | #ifdef SQLITE_HAVE_ZLIB |
| @@ -20550,10 +20929,11 @@ | |
| 20550 | ** \s -> space |
| 20551 | ** \" -> " |
| 20552 | ** \' -> ' |
| 20553 | ** \\ -> backslash |
| 20554 | ** \NNN -> ascii character NNN in octal |
| 20555 | */ |
| 20556 | static void resolve_backslashes(char *z){ |
| 20557 | int i, j; |
| 20558 | char c; |
| 20559 | while( *z && *z!='\\' ) z++; |
| @@ -20578,10 +20958,19 @@ | |
| 20578 | c = '"'; |
| 20579 | }else if( c=='\'' ){ |
| 20580 | c = '\''; |
| 20581 | }else if( c=='\\' ){ |
| 20582 | c = '\\'; |
| 20583 | }else if( c>='0' && c<='7' ){ |
| 20584 | c -= '0'; |
| 20585 | if( z[i+1]>='0' && z[i+1]<='7' ){ |
| 20586 | i++; |
| 20587 | c = (c<<3) + z[i] - '0'; |
| @@ -20677,11 +21066,11 @@ | |
| 20677 | if( p->traceOut==0 ) return 0; |
| 20678 | if( mType==SQLITE_TRACE_CLOSE ){ |
| 20679 | utf8_printf(p->traceOut, "-- closing database connection\n"); |
| 20680 | return 0; |
| 20681 | } |
| 20682 | if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){ |
| 20683 | zSql = (const char*)pX; |
| 20684 | }else{ |
| 20685 | pStmt = (sqlite3_stmt*)pP; |
| 20686 | switch( p->eTraceType ){ |
| 20687 | case SHELL_TRACE_EXPANDED: { |
| @@ -20709,11 +21098,11 @@ | |
| 20709 | case SQLITE_TRACE_STMT: { |
| 20710 | utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 20711 | break; |
| 20712 | } |
| 20713 | case SQLITE_TRACE_PROFILE: { |
| 20714 | sqlite3_int64 nNanosec = *(sqlite3_int64*)pX; |
| 20715 | utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 20716 | break; |
| 20717 | } |
| 20718 | } |
| 20719 | return 0; |
| @@ -20785,12 +21174,12 @@ | |
| 20785 | ** EOF on end-of-file. |
| 20786 | ** + Report syntax errors on stderr |
| 20787 | */ |
| 20788 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 20789 | int c; |
| 20790 | int cSep = p->cColSep; |
| 20791 | int rSep = p->cRowSep; |
| 20792 | p->n = 0; |
| 20793 | c = fgetc(p->in); |
| 20794 | if( c==EOF || seenInterrupt ){ |
| 20795 | p->cTerm = EOF; |
| 20796 | return 0; |
| @@ -20875,12 +21264,12 @@ | |
| 20875 | ** EOF on end-of-file. |
| 20876 | ** + Report syntax errors on stderr |
| 20877 | */ |
| 20878 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 20879 | int c; |
| 20880 | int cSep = p->cColSep; |
| 20881 | int rSep = p->cRowSep; |
| 20882 | p->n = 0; |
| 20883 | c = fgetc(p->in); |
| 20884 | if( c==EOF || seenInterrupt ){ |
| 20885 | p->cTerm = EOF; |
| 20886 | return 0; |
| @@ -21070,11 +21459,11 @@ | |
| 21070 | } |
| 21071 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 21072 | zName = sqlite3_column_text(pQuery, 0); |
| 21073 | zSql = sqlite3_column_text(pQuery, 1); |
| 21074 | if( zName==0 || zSql==0 ) continue; |
| 21075 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ) continue; |
| 21076 | printf("%s... ", zName); fflush(stdout); |
| 21077 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 21078 | if( zErrMsg ){ |
| 21079 | utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 21080 | sqlite3_free(zErrMsg); |
| @@ -21237,11 +21626,13 @@ | |
| 21237 | } |
| 21238 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 21239 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| 21240 | && sqlite3_column_bytes(pStmt,0)>100 |
| 21241 | ){ |
| 21242 | memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100); |
| 21243 | sqlite3_finalize(pStmt); |
| 21244 | }else{ |
| 21245 | raw_printf(stderr, "unable to read database header\n"); |
| 21246 | sqlite3_finalize(pStmt); |
| 21247 | return 1; |
| @@ -22054,11 +22445,14 @@ | |
| 22054 | if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR; |
| 22055 | } |
| 22056 | } |
| 22057 | } |
| 22058 | } |
| 22059 | |
| 22060 | return SQLITE_OK; |
| 22061 | } |
| 22062 | |
| 22063 | /* |
| 22064 | ** This function assumes that all arguments within the ArCommand.azArg[] |
| @@ -22820,11 +23214,11 @@ | |
| 22820 | rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0); |
| 22821 | rc_err_oom_die(rc); |
| 22822 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 22823 | rc = sqlite3_step(pStmt); |
| 22824 | sqlite3_finalize(pStmt); |
| 22825 | assert(rc==SQLITE_DONE); |
| 22826 | } |
| 22827 | assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ |
| 22828 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 22829 | rc_err_oom_die(rc); |
| 22830 | rc = sqlite3_step(pStmt); |
| @@ -23752,12 +24146,12 @@ | |
| 23752 | if( nSep>1 ){ |
| 23753 | raw_printf(stderr, "Error: multi-character row separators not allowed" |
| 23754 | " for import\n"); |
| 23755 | goto meta_command_exit; |
| 23756 | } |
| 23757 | sCtx.cColSep = p->colSeparator[0]; |
| 23758 | sCtx.cRowSep = p->rowSeparator[0]; |
| 23759 | } |
| 23760 | sCtx.zFile = zFile; |
| 23761 | sCtx.nLine = 1; |
| 23762 | if( sCtx.zFile[0]=='|' ){ |
| 23763 | #ifdef SQLITE_OMIT_POPEN |
| @@ -23949,10 +24343,16 @@ | |
| 23949 | sqlite3_stmt *pStmt; |
| 23950 | int tnum = 0; |
| 23951 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 23952 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 23953 | int i; |
| 23954 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| 23955 | utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" |
| 23956 | " .imposter off\n"); |
| 23957 | /* Also allowed, but not documented: |
| 23958 | ** |
| @@ -24133,11 +24533,12 @@ | |
| 24133 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE) |
| 24134 | if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){ |
| 24135 | const char *zFile, *zProc; |
| 24136 | char *zErrMsg = 0; |
| 24137 | failIfSafeMode(p, "cannot run .load in safe mode"); |
| 24138 | if( nArg<2 ){ |
| 24139 | raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); |
| 24140 | rc = 1; |
| 24141 | goto meta_command_exit; |
| 24142 | } |
| 24143 | zFile = azArg[1]; |
| @@ -25457,32 +25858,40 @@ | |
| 25457 | "' OR ') as query, tname from tabcols group by tname)" |
| 25458 | , zRevText); |
| 25459 | shell_check_oom(zRevText); |
| 25460 | if( bDebug ) utf8_printf(p->out, "%s\n", zRevText); |
| 25461 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 25462 | assert(lrc==SQLITE_OK); |
| 25463 | if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC); |
| 25464 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 25465 | if( lrc ){ |
| 25466 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 25467 | sqlite3_stmt *pCheckStmt; |
| 25468 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 25469 | if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery); |
| 25470 | if( SQLITE_OK==lrc ){ |
| 25471 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 25472 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 25473 | if( countIrreversible>0 ){ |
| 25474 | int sz = (int)(countIrreversible + 0.5); |
| 25475 | utf8_printf(stderr, |
| 25476 | "Digest includes %d invalidly encoded text field%s.\n", |
| 25477 | sz, (sz>1)? "s": ""); |
| 25478 | } |
| 25479 | } |
| 25480 | sqlite3_finalize(pCheckStmt); |
| 25481 | } |
| 25482 | sqlite3_finalize(pStmt); |
| 25483 | } |
| 25484 | sqlite3_free(zRevText); |
| 25485 | } |
| 25486 | #endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */ |
| 25487 | sqlite3_free(zSql); |
| 25488 | }else |
| @@ -25741,10 +26150,16 @@ | |
| 25741 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 25742 | int isOk = 0; |
| 25743 | int i, n2; |
| 25744 | const char *zCmd = 0; |
| 25745 | |
| 25746 | open_db(p, 0); |
| 25747 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 25748 | |
| 25749 | /* The argument can optionally begin with "-" or "--" */ |
| 25750 | if( zCmd[0]=='-' && zCmd[1] ){ |
| @@ -26740,10 +27155,14 @@ | |
| 26740 | " -sorterref SIZE sorter references threshold size\n" |
| 26741 | #endif |
| 26742 | " -stats print memory stats before each finalize\n" |
| 26743 | " -table set output mode to 'table'\n" |
| 26744 | " -tabs set output mode to 'tabs'\n" |
| 26745 | " -version show SQLite version\n" |
| 26746 | " -vfs NAME use NAME as the default VFS\n" |
| 26747 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 26748 | " -vfstrace enable tracing of all VFS calls\n" |
| 26749 | #endif |
| @@ -26831,10 +27250,14 @@ | |
| 26831 | argv[0], argv[argc-1]); |
| 26832 | exit(1); |
| 26833 | } |
| 26834 | return argv[i]; |
| 26835 | } |
| 26836 | |
| 26837 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 26838 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 26839 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 26840 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -26852,11 +27275,11 @@ | |
| 26852 | #else |
| 26853 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 26854 | char **argv; |
| 26855 | #endif |
| 26856 | #ifdef SQLITE_DEBUG |
| 26857 | sqlite3_int64 mem_main_enter = sqlite3_memory_used(); |
| 26858 | #endif |
| 26859 | char *zErrMsg = 0; |
| 26860 | #ifdef SQLITE_SHELL_FIDDLE |
| 26861 | # define data shellState |
| 26862 | #else |
| @@ -26873,22 +27296,27 @@ | |
| 26873 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 26874 | #if !SQLITE_SHELL_IS_UTF8 |
| 26875 | char **argvToFree = 0; |
| 26876 | int argcToFree = 0; |
| 26877 | #endif |
| 26878 | |
| 26879 | setBinaryMode(stdin, 0); |
| 26880 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 26881 | #ifdef SQLITE_SHELL_FIDDLE |
| 26882 | stdin_is_interactive = 0; |
| 26883 | stdout_is_console = 1; |
| 26884 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 26885 | #else |
| 26886 | stdin_is_interactive = isatty(0); |
| 26887 | stdout_is_console = isatty(1); |
| 26888 | #endif |
| 26889 | |
| 26890 | #if !defined(_WIN32_WCE) |
| 26891 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 26892 | if( isatty(0) && isatty(2) ){ |
| 26893 | fprintf(stderr, |
| 26894 | "attach debugger to process %d and press any key to continue.\n", |
| @@ -26904,10 +27332,18 @@ | |
| 26904 | #elif defined(SIGTRAP) |
| 26905 | raise(SIGTRAP); |
| 26906 | #endif |
| 26907 | } |
| 26908 | } |
| 26909 | #endif |
| 26910 | |
| 26911 | #if USE_SYSTEM_SQLITE+0!=1 |
| 26912 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 26913 | utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| @@ -26944,19 +27380,10 @@ | |
| 26944 | #endif |
| 26945 | |
| 26946 | assert( argc>=1 && argv && argv[0] ); |
| 26947 | Argv0 = argv[0]; |
| 26948 | |
| 26949 | /* Make sure we have a valid signal handler early, before anything |
| 26950 | ** else is done. |
| 26951 | */ |
| 26952 | #ifdef SIGINT |
| 26953 | signal(SIGINT, interrupt_handler); |
| 26954 | #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 26955 | SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); |
| 26956 | #endif |
| 26957 | |
| 26958 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 26959 | { |
| 26960 | /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name |
| 26961 | ** of a C-function that will provide the name of the database file. Use |
| 26962 | ** this compile-time option to embed this shell program in larger |
| @@ -27109,10 +27536,12 @@ | |
| 27109 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 27110 | bail_on_error = 1; |
| 27111 | }else if( cli_strcmp(z,"-nonce")==0 ){ |
| 27112 | free(data.zNonce); |
| 27113 | data.zNonce = strdup(argv[++i]); |
| 27114 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27115 | /* no-op - catch this on the second pass */ |
| 27116 | } |
| 27117 | } |
| 27118 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -27271,10 +27700,14 @@ | |
| 27271 | return 0; |
| 27272 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 27273 | stdin_is_interactive = 1; |
| 27274 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 27275 | stdin_is_interactive = 0; |
| 27276 | }else if( cli_strcmp(z,"-heap")==0 ){ |
| 27277 | i++; |
| 27278 | }else if( cli_strcmp(z,"-pagecache")==0 ){ |
| 27279 | i+=2; |
| 27280 | }else if( cli_strcmp(z,"-lookaside")==0 ){ |
| @@ -27341,17 +27774,27 @@ | |
| 27341 | readStdin = 0; |
| 27342 | break; |
| 27343 | #endif |
| 27344 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27345 | data.bSafeMode = data.bSafeModePersist = 1; |
| 27346 | }else{ |
| 27347 | utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 27348 | raw_printf(stderr,"Use -help for a list of options.\n"); |
| 27349 | return 1; |
| 27350 | } |
| 27351 | data.cMode = data.mode; |
| 27352 | } |
| 27353 | |
| 27354 | if( !readStdin ){ |
| 27355 | /* Run all arguments that do not begin with '-' as if they were separate |
| 27356 | ** command-line inputs, except for the argToSkip argument which contains |
| 27357 | ** the database filename. |
| 27358 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -244,10 +244,11 @@ | |
| 244 | |
| 245 | #if defined(_WIN32) || defined(WIN32) |
| 246 | #if SQLITE_OS_WINRT |
| 247 | #include <intrin.h> |
| 248 | #endif |
| 249 | #define WIN32_LEAN_AND_MEAN |
| 250 | #include <windows.h> |
| 251 | |
| 252 | /* string conversion routines only needed on Win32 */ |
| 253 | extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
| 254 | extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); |
| @@ -458,14 +459,28 @@ | |
| 459 | ** at an error if we are not interactive. |
| 460 | */ |
| 461 | static int bail_on_error = 0; |
| 462 | |
| 463 | /* |
| 464 | ** Treat stdin as an interactive input if the following variable |
| 465 | ** is true. Otherwise, assume stdin is connected to a file or pipe. |
| 466 | */ |
| 467 | static int stdin_is_interactive = 1; |
| 468 | |
| 469 | #if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \ |
| 470 | && !defined(SHELL_OMIT_WIN_UTF8) |
| 471 | # define SHELL_WIN_UTF8_OPT 1 |
| 472 | #else |
| 473 | # define SHELL_WIN_UTF8_OPT 0 |
| 474 | #endif |
| 475 | |
| 476 | #if SHELL_WIN_UTF8_OPT |
| 477 | /* |
| 478 | ** Setup console for UTF-8 input/output when following variable true. |
| 479 | */ |
| 480 | static int console_utf8 = 0; |
| 481 | #endif |
| 482 | |
| 483 | /* |
| 484 | ** On Windows systems we have to know if standard output is a console |
| 485 | ** in order to translate UTF-8 into MBCS. The following variable is |
| 486 | ** true if translation is required. |
| @@ -595,20 +610,154 @@ | |
| 610 | } |
| 611 | return dynPrompt.dynamicPrompt; |
| 612 | } |
| 613 | #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ |
| 614 | |
| 615 | #if SHELL_WIN_UTF8_OPT |
| 616 | /* Following struct is used for -utf8 operation. */ |
| 617 | static struct ConsoleState { |
| 618 | int stdinEof; /* EOF has been seen on console input */ |
| 619 | int infsMode; /* Input file stream mode upon shell start */ |
| 620 | UINT inCodePage; /* Input code page upon shell start */ |
| 621 | UINT outCodePage; /* Output code page upon shell start */ |
| 622 | HANDLE hConsoleIn; /* Console input handle */ |
| 623 | DWORD consoleMode; /* Console mode upon shell start */ |
| 624 | } conState = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 }; |
| 625 | |
| 626 | #ifndef _O_U16TEXT /* For build environments lacking this constant: */ |
| 627 | # define _O_U16TEXT 0x20000 |
| 628 | #endif |
| 629 | |
| 630 | /* |
| 631 | ** Prepare console, (if known to be a WIN32 console), for UTF-8 |
| 632 | ** input (from either typing or suitable paste operations) and for |
| 633 | ** UTF-8 rendering. This may "fail" with a message to stderr, where |
| 634 | ** the preparation is not done and common "code page" issues occur. |
| 635 | */ |
| 636 | static void console_prepare(void){ |
| 637 | HANDLE hCI = GetStdHandle(STD_INPUT_HANDLE); |
| 638 | DWORD consoleMode = 0; |
| 639 | if( isatty(0) && GetFileType(hCI)==FILE_TYPE_CHAR |
| 640 | && GetConsoleMode( hCI, &consoleMode) ){ |
| 641 | if( !IsValidCodePage(CP_UTF8) ){ |
| 642 | fprintf(stderr, "Cannot use UTF-8 code page.\n"); |
| 643 | console_utf8 = 0; |
| 644 | return; |
| 645 | } |
| 646 | conState.hConsoleIn = hCI; |
| 647 | conState.consoleMode = consoleMode; |
| 648 | conState.inCodePage = GetConsoleCP(); |
| 649 | conState.outCodePage = GetConsoleOutputCP(); |
| 650 | SetConsoleCP(CP_UTF8); |
| 651 | SetConsoleOutputCP(CP_UTF8); |
| 652 | consoleMode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; |
| 653 | SetConsoleMode(conState.hConsoleIn, consoleMode); |
| 654 | conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT); |
| 655 | console_utf8 = 1; |
| 656 | }else{ |
| 657 | console_utf8 = 0; |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | /* |
| 662 | ** Undo the effects of console_prepare(), if any. |
| 663 | */ |
| 664 | static void SQLITE_CDECL console_restore(void){ |
| 665 | if( console_utf8 && conState.inCodePage!=0 |
| 666 | && conState.hConsoleIn!=INVALID_HANDLE_VALUE ){ |
| 667 | _setmode(_fileno(stdin), conState.infsMode); |
| 668 | SetConsoleCP(conState.inCodePage); |
| 669 | SetConsoleOutputCP(conState.outCodePage); |
| 670 | SetConsoleMode(conState.hConsoleIn, conState.consoleMode); |
| 671 | /* Avoid multiple calls. */ |
| 672 | conState.hConsoleIn = INVALID_HANDLE_VALUE; |
| 673 | conState.consoleMode = 0; |
| 674 | console_utf8 = 0; |
| 675 | } |
| 676 | } |
| 677 | |
| 678 | /* |
| 679 | ** Collect input like fgets(...) with special provisions for input |
| 680 | ** from the Windows console to get around its strange coding issues. |
| 681 | ** Defers to plain fgets() when input is not interactive or when the |
| 682 | ** startup option, -utf8, has not been provided or taken effect. |
| 683 | */ |
| 684 | static char* utf8_fgets(char *buf, int ncmax, FILE *fin){ |
| 685 | if( fin==0 ) fin = stdin; |
| 686 | if( fin==stdin && stdin_is_interactive && console_utf8 ){ |
| 687 | # define SQLITE_IALIM 150 |
| 688 | wchar_t wbuf[SQLITE_IALIM]; |
| 689 | int lend = 0; |
| 690 | int noc = 0; |
| 691 | if( ncmax==0 || conState.stdinEof ) return 0; |
| 692 | buf[0] = 0; |
| 693 | while( noc<ncmax-7-1 && !lend ){ |
| 694 | /* There is room for at least 2 more characters and a 0-terminator. */ |
| 695 | int na = (ncmax > SQLITE_IALIM*4+1 + noc) |
| 696 | ? SQLITE_IALIM : (ncmax-1 - noc)/4; |
| 697 | # undef SQLITE_IALIM |
| 698 | DWORD nbr = 0; |
| 699 | BOOL bRC = ReadConsoleW(conState.hConsoleIn, wbuf, na, &nbr, 0); |
| 700 | if( !bRC || (noc==0 && nbr==0) ) return 0; |
| 701 | if( nbr > 0 ){ |
| 702 | int nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, |
| 703 | wbuf,nbr,0,0,0,0); |
| 704 | if( nmb !=0 && noc+nmb <= ncmax ){ |
| 705 | int iseg = noc; |
| 706 | nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR, |
| 707 | wbuf,nbr,buf+noc,nmb,0,0); |
| 708 | noc += nmb; |
| 709 | /* Fixup line-ends as coded by Windows for CR (or "Enter".)*/ |
| 710 | if( noc > 0 ){ |
| 711 | if( buf[noc-1]=='\n' ){ |
| 712 | lend = 1; |
| 713 | if( noc > 1 && buf[noc-2]=='\r' ){ |
| 714 | buf[noc-2] = '\n'; |
| 715 | --noc; |
| 716 | } |
| 717 | } |
| 718 | } |
| 719 | /* Check for ^Z (anywhere in line) too. */ |
| 720 | while( iseg < noc ){ |
| 721 | if( buf[iseg]==0x1a ){ |
| 722 | conState.stdinEof = 1; |
| 723 | noc = iseg; /* Chop ^Z and anything following. */ |
| 724 | break; |
| 725 | } |
| 726 | ++iseg; |
| 727 | } |
| 728 | }else break; /* Drop apparent garbage in. (Could assert.) */ |
| 729 | }else break; |
| 730 | } |
| 731 | /* If got nothing, (after ^Z chop), must be at end-of-file. */ |
| 732 | if( noc == 0 ) return 0; |
| 733 | buf[noc] = 0; |
| 734 | return buf; |
| 735 | }else{ |
| 736 | return fgets(buf, ncmax, fin); |
| 737 | } |
| 738 | } |
| 739 | |
| 740 | # define fgets(b,n,f) utf8_fgets(b,n,f) |
| 741 | #endif /* SHELL_WIN_UTF8_OPT */ |
| 742 | |
| 743 | /* |
| 744 | ** Render output like fprintf(). Except, if the output is going to the |
| 745 | ** console and if this is running on a Windows machine, and if the -utf8 |
| 746 | ** option is unavailable or (available and inactive), translate the |
| 747 | ** output from UTF-8 into MBCS for output through 8-bit stdout stream. |
| 748 | ** (With -utf8 active, no translation is needed and must not be done.) |
| 749 | */ |
| 750 | #if defined(_WIN32) || defined(WIN32) |
| 751 | void utf8_printf(FILE *out, const char *zFormat, ...){ |
| 752 | va_list ap; |
| 753 | va_start(ap, zFormat); |
| 754 | if( stdout_is_console && (out==stdout || out==stderr) |
| 755 | # if SHELL_WIN_UTF8_OPT |
| 756 | && !console_utf8 |
| 757 | # endif |
| 758 | ){ |
| 759 | char *z1 = sqlite3_vmprintf(zFormat, ap); |
| 760 | char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); |
| 761 | sqlite3_free(z1); |
| 762 | fputs(z2, out); |
| 763 | sqlite3_free(z2); |
| @@ -636,11 +785,11 @@ | |
| 785 | } |
| 786 | |
| 787 | /* Check a pointer to see if it is NULL. If it is NULL, exit with an |
| 788 | ** out-of-memory error. |
| 789 | */ |
| 790 | static void shell_check_oom(const void *p){ |
| 791 | if( p==0 ) shell_out_of_memory(); |
| 792 | } |
| 793 | |
| 794 | /* |
| 795 | ** Write I/O traces to the following stream. |
| @@ -815,13 +964,18 @@ | |
| 964 | zLine[n] = 0; |
| 965 | break; |
| 966 | } |
| 967 | } |
| 968 | #if defined(_WIN32) || defined(WIN32) |
| 969 | /* For interactive input on Windows systems, without -utf8, |
| 970 | ** translate the multi-byte characterset characters into UTF-8. |
| 971 | ** This is the translation that predates the -utf8 option. */ |
| 972 | if( stdin_is_interactive && in==stdin |
| 973 | # if SHELL_WIN_UTF8_OPT |
| 974 | && !console_utf8 |
| 975 | # endif /* SHELL_WIN_UTF8_OPT */ |
| 976 | ){ |
| 977 | char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); |
| 978 | if( zTrans ){ |
| 979 | i64 nTrans = strlen(zTrans)+1; |
| 980 | if( nTrans>nLine ){ |
| 981 | zLine = realloc(zLine, nTrans); |
| @@ -858,14 +1012,25 @@ | |
| 1012 | }else{ |
| 1013 | zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt; |
| 1014 | #if SHELL_USE_LOCAL_GETLINE |
| 1015 | printf("%s", zPrompt); |
| 1016 | fflush(stdout); |
| 1017 | do{ |
| 1018 | zResult = local_getline(zPrior, stdin); |
| 1019 | zPrior = 0; |
| 1020 | /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ |
| 1021 | if( zResult==0 ) sqlite3_sleep(50); |
| 1022 | }while( zResult==0 && seenInterrupt>0 ); |
| 1023 | #else |
| 1024 | free(zPrior); |
| 1025 | zResult = shell_readline(zPrompt); |
| 1026 | while( zResult==0 ){ |
| 1027 | /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ |
| 1028 | sqlite3_sleep(50); |
| 1029 | if( seenInterrupt==0 ) break; |
| 1030 | zResult = shell_readline(""); |
| 1031 | } |
| 1032 | if( zResult && *zResult ) shell_add_history(zResult); |
| 1033 | #endif |
| 1034 | } |
| 1035 | return zResult; |
| 1036 | } |
| @@ -1001,10 +1166,11 @@ | |
| 1166 | ** |
| 1167 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1168 | */ |
| 1169 | static char quoteChar(const char *zName){ |
| 1170 | int i; |
| 1171 | if( zName==0 ) return '"'; |
| 1172 | if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; |
| 1173 | for(i=0; zName[i]; i++){ |
| 1174 | if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; |
| 1175 | } |
| 1176 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| @@ -1644,11 +1810,12 @@ | |
| 1810 | ** May you find forgiveness for yourself and forgive others. |
| 1811 | ** May you share freely, never taking more than you give. |
| 1812 | ** |
| 1813 | ****************************************************************************** |
| 1814 | ** |
| 1815 | ** This SQLite extension implements functions that compute SHA3 hashes |
| 1816 | ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. |
| 1817 | ** Two SQL functions are implemented: |
| 1818 | ** |
| 1819 | ** sha3(X,SIZE) |
| 1820 | ** sha3_query(Y,SIZE) |
| 1821 | ** |
| @@ -3244,22 +3411,22 @@ | |
| 3411 | *pOut = 0; |
| 3412 | return pOut; |
| 3413 | } |
| 3414 | |
| 3415 | /* Skip over text which is not base64 numeral(s). */ |
| 3416 | static char * skipNonB64( char *s, int nc ){ |
| 3417 | char c; |
| 3418 | while( nc-- > 0 && (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s; |
| 3419 | return s; |
| 3420 | } |
| 3421 | |
| 3422 | /* Decode base64 text into a byte buffer. */ |
| 3423 | static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){ |
| 3424 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3425 | while( ncIn>0 && *pIn!=PAD_CHAR ){ |
| 3426 | static signed char nboi[] = { 0, 0, 1, 2, 3 }; |
| 3427 | char *pUse = skipNonB64(pIn, ncIn); |
| 3428 | unsigned long qv = 0L; |
| 3429 | int nti, nbo, nac; |
| 3430 | ncIn -= (pUse - pIn); |
| 3431 | pIn = pUse; |
| 3432 | nti = (ncIn>4)? 4 : ncIn; |
| @@ -3314,14 +3481,21 @@ | |
| 3481 | nc = 4*(nv+2/3); /* quads needed */ |
| 3482 | nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */ |
| 3483 | if( nvMax < nc ){ |
| 3484 | sqlite3_result_error(context, "blob expanded to base64 too big", -1); |
| 3485 | return; |
| 3486 | } |
| 3487 | bBuf = (u8*)sqlite3_value_blob(av[0]); |
| 3488 | if( !bBuf ){ |
| 3489 | if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ |
| 3490 | goto memFail; |
| 3491 | } |
| 3492 | sqlite3_result_text(context,"",-1,SQLITE_STATIC); |
| 3493 | break; |
| 3494 | } |
| 3495 | cBuf = sqlite3_malloc(nc); |
| 3496 | if( !cBuf ) goto memFail; |
| 3497 | nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf); |
| 3498 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3499 | break; |
| 3500 | case SQLITE_TEXT: |
| 3501 | nc = nv; |
| @@ -3329,14 +3503,21 @@ | |
| 3503 | if( nvMax < nb ){ |
| 3504 | sqlite3_result_error(context, "blob from base64 may be too big", -1); |
| 3505 | return; |
| 3506 | }else if( nb<1 ){ |
| 3507 | nb = 1; |
| 3508 | } |
| 3509 | cBuf = (char *)sqlite3_value_text(av[0]); |
| 3510 | if( !cBuf ){ |
| 3511 | if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ |
| 3512 | goto memFail; |
| 3513 | } |
| 3514 | sqlite3_result_zeroblob(context, 0); |
| 3515 | break; |
| 3516 | } |
| 3517 | bBuf = sqlite3_malloc(nb); |
| 3518 | if( !bBuf ) goto memFail; |
| 3519 | nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf); |
| 3520 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3521 | break; |
| 3522 | default: |
| 3523 | sqlite3_result_error(context, "base64 accepts only blob or text", -1); |
| @@ -3520,13 +3701,13 @@ | |
| 3701 | |
| 3702 | /* Width of base64 lines. Should be an integer multiple of 5. */ |
| 3703 | #define B85_DARK_MAX 80 |
| 3704 | |
| 3705 | |
| 3706 | static char * skipNonB85( char *s, int nc ){ |
| 3707 | char c; |
| 3708 | while( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s; |
| 3709 | return s; |
| 3710 | } |
| 3711 | |
| 3712 | /* Convert small integer, known to be in 0..84 inclusive, to base85 numeral. |
| 3713 | * Do not use the macro form with argument expression having a side-effect.*/ |
| @@ -3592,11 +3773,11 @@ | |
| 3773 | /* Decode base85 text into a byte buffer. */ |
| 3774 | static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){ |
| 3775 | if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; |
| 3776 | while( ncIn>0 ){ |
| 3777 | static signed char nboi[] = { 0, 0, 1, 2, 3, 4 }; |
| 3778 | char *pUse = skipNonB85(pIn, ncIn); |
| 3779 | unsigned long qv = 0L; |
| 3780 | int nti, nbo; |
| 3781 | ncIn -= (pUse - pIn); |
| 3782 | pIn = pUse; |
| 3783 | nti = (ncIn>5)? 5 : ncIn; |
| @@ -3676,14 +3857,21 @@ | |
| 3857 | /* ulongs tail newlines tailenc+nul*/ |
| 3858 | nc = 5*(nv/4) + nv%4 + nv/64+1 + 2; |
| 3859 | if( nvMax < nc ){ |
| 3860 | sqlite3_result_error(context, "blob expanded to base85 too big", -1); |
| 3861 | return; |
| 3862 | } |
| 3863 | bBuf = (u8*)sqlite3_value_blob(av[0]); |
| 3864 | if( !bBuf ){ |
| 3865 | if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ |
| 3866 | goto memFail; |
| 3867 | } |
| 3868 | sqlite3_result_text(context,"",-1,SQLITE_STATIC); |
| 3869 | break; |
| 3870 | } |
| 3871 | cBuf = sqlite3_malloc(nc); |
| 3872 | if( !cBuf ) goto memFail; |
| 3873 | nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf); |
| 3874 | sqlite3_result_text(context, cBuf, nc, sqlite3_free); |
| 3875 | break; |
| 3876 | case SQLITE_TEXT: |
| 3877 | nc = nv; |
| @@ -3691,14 +3879,21 @@ | |
| 3879 | if( nvMax < nb ){ |
| 3880 | sqlite3_result_error(context, "blob from base85 may be too big", -1); |
| 3881 | return; |
| 3882 | }else if( nb<1 ){ |
| 3883 | nb = 1; |
| 3884 | } |
| 3885 | cBuf = (char *)sqlite3_value_text(av[0]); |
| 3886 | if( !cBuf ){ |
| 3887 | if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ |
| 3888 | goto memFail; |
| 3889 | } |
| 3890 | sqlite3_result_zeroblob(context, 0); |
| 3891 | break; |
| 3892 | } |
| 3893 | bBuf = sqlite3_malloc(nb); |
| 3894 | if( !bBuf ) goto memFail; |
| 3895 | nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf); |
| 3896 | sqlite3_result_blob(context, bBuf, nb, sqlite3_free); |
| 3897 | break; |
| 3898 | default: |
| 3899 | sqlite3_result_error(context, "base85 accepts only blob or text.", -1); |
| @@ -4114,11 +4309,11 @@ | |
| 4309 | } |
| 4310 | |
| 4311 | /************************* End ../ext/misc/ieee754.c ********************/ |
| 4312 | /************************* Begin ../ext/misc/series.c ******************/ |
| 4313 | /* |
| 4314 | ** 2015-08-18, 2023-04-28 |
| 4315 | ** |
| 4316 | ** The author disclaims copyright to this source code. In place of |
| 4317 | ** a legal notice, here is a blessing: |
| 4318 | ** |
| 4319 | ** May you do good and not evil. |
| @@ -4127,11 +4322,23 @@ | |
| 4322 | ** |
| 4323 | ************************************************************************* |
| 4324 | ** |
| 4325 | ** This file demonstrates how to create a table-valued-function using |
| 4326 | ** a virtual table. This demo implements the generate_series() function |
| 4327 | ** which gives the same results as the eponymous function in PostgreSQL, |
| 4328 | ** within the limitation that its arguments are signed 64-bit integers. |
| 4329 | ** |
| 4330 | ** Considering its equivalents to generate_series(start,stop,step): A |
| 4331 | ** value V[n] sequence is produced for integer n ascending from 0 where |
| 4332 | ** ( V[n] == start + n * step && sgn(V[n] - stop) * sgn(step) >= 0 ) |
| 4333 | ** for each produced value (independent of production time ordering.) |
| 4334 | ** |
| 4335 | ** All parameters must be either integer or convertable to integer. |
| 4336 | ** The start parameter is required. |
| 4337 | ** The stop parameter defaults to (1<<32)-1 (aka 4294967295 or 0xffffffff) |
| 4338 | ** The step parameter defaults to 1 and 0 is treated as 1. |
| 4339 | ** |
| 4340 | ** Examples: |
| 4341 | ** |
| 4342 | ** SELECT * FROM generate_series(0,100,5); |
| 4343 | ** |
| 4344 | ** The query above returns integers from 0 through 100 counting by steps |
| @@ -4143,10 +4350,18 @@ | |
| 4350 | ** |
| 4351 | ** SELECT * FROM generate_series(20) LIMIT 10; |
| 4352 | ** |
| 4353 | ** Integers 20 through 29. |
| 4354 | ** |
| 4355 | ** SELECT * FROM generate_series(0,-100,-5); |
| 4356 | ** |
| 4357 | ** Integers 0 -5 -10 ... -100. |
| 4358 | ** |
| 4359 | ** SELECT * FROM generate_series(0,-1); |
| 4360 | ** |
| 4361 | ** Empty sequence. |
| 4362 | ** |
| 4363 | ** HOW IT WORKS |
| 4364 | ** |
| 4365 | ** The generate_series "function" is really a virtual table with the |
| 4366 | ** following schema: |
| 4367 | ** |
| @@ -4154,10 +4369,13 @@ | |
| 4369 | ** value, |
| 4370 | ** start HIDDEN, |
| 4371 | ** stop HIDDEN, |
| 4372 | ** step HIDDEN |
| 4373 | ** ); |
| 4374 | ** |
| 4375 | ** The virtual table also has a rowid, logically equivalent to n+1 where |
| 4376 | ** "n" is the ascending integer in the aforesaid production definition. |
| 4377 | ** |
| 4378 | ** Function arguments in queries against this virtual table are translated |
| 4379 | ** into equality constraints against successive hidden columns. In other |
| 4380 | ** words, the following pairs of queries are equivalent to each other: |
| 4381 | ** |
| @@ -4187,27 +4405,139 @@ | |
| 4405 | */ |
| 4406 | /* #include "sqlite3ext.h" */ |
| 4407 | SQLITE_EXTENSION_INIT1 |
| 4408 | #include <assert.h> |
| 4409 | #include <string.h> |
| 4410 | #include <limits.h> |
| 4411 | |
| 4412 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 4413 | /* |
| 4414 | ** Return that member of a generate_series(...) sequence whose 0-based |
| 4415 | ** index is ix. The 0th member is given by smBase. The sequence members |
| 4416 | ** progress per ix increment by smStep. |
| 4417 | */ |
| 4418 | static sqlite3_int64 genSeqMember(sqlite3_int64 smBase, |
| 4419 | sqlite3_int64 smStep, |
| 4420 | sqlite3_uint64 ix){ |
| 4421 | if( ix>=(sqlite3_uint64)LLONG_MAX ){ |
| 4422 | /* Get ix into signed i64 range. */ |
| 4423 | ix -= (sqlite3_uint64)LLONG_MAX; |
| 4424 | /* With 2's complement ALU, this next can be 1 step, but is split into |
| 4425 | * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ |
| 4426 | smBase += (LLONG_MAX/2) * smStep; |
| 4427 | smBase += (LLONG_MAX - LLONG_MAX/2) * smStep; |
| 4428 | } |
| 4429 | /* Under UBSAN (or on 1's complement machines), must do this last term |
| 4430 | * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ |
| 4431 | if( ix>=2 ){ |
| 4432 | sqlite3_int64 ix2 = (sqlite3_int64)ix/2; |
| 4433 | smBase += ix2*smStep; |
| 4434 | ix -= ix2; |
| 4435 | } |
| 4436 | return smBase + ((sqlite3_int64)ix)*smStep; |
| 4437 | } |
| 4438 | |
| 4439 | /* typedef unsigned char u8; */ |
| 4440 | |
| 4441 | typedef struct SequenceSpec { |
| 4442 | sqlite3_int64 iBase; /* Starting value ("start") */ |
| 4443 | sqlite3_int64 iTerm; /* Given terminal value ("stop") */ |
| 4444 | sqlite3_int64 iStep; /* Increment ("step") */ |
| 4445 | sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */ |
| 4446 | sqlite3_uint64 uSeqIndexNow; /* Current index during generation */ |
| 4447 | sqlite3_int64 iValueNow; /* Current value during generation */ |
| 4448 | u8 isNotEOF; /* Sequence generation not exhausted */ |
| 4449 | u8 isReversing; /* Sequence is being reverse generated */ |
| 4450 | } SequenceSpec; |
| 4451 | |
| 4452 | /* |
| 4453 | ** Prepare a SequenceSpec for use in generating an integer series |
| 4454 | ** given initialized iBase, iTerm and iStep values. Sequence is |
| 4455 | ** initialized per given isReversing. Other members are computed. |
| 4456 | */ |
| 4457 | static void setupSequence( SequenceSpec *pss ){ |
| 4458 | int bSameSigns; |
| 4459 | pss->uSeqIndexMax = 0; |
| 4460 | pss->isNotEOF = 0; |
| 4461 | bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0); |
| 4462 | if( pss->iTerm < pss->iBase ){ |
| 4463 | sqlite3_uint64 nuspan = 0; |
| 4464 | if( bSameSigns ){ |
| 4465 | nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm); |
| 4466 | }else{ |
| 4467 | /* Under UBSAN (or on 1's complement machines), must do this in steps. |
| 4468 | * In this clause, iBase>=0 and iTerm<0 . */ |
| 4469 | nuspan = 1; |
| 4470 | nuspan += pss->iBase; |
| 4471 | nuspan += -(pss->iTerm+1); |
| 4472 | } |
| 4473 | if( pss->iStep<0 ){ |
| 4474 | pss->isNotEOF = 1; |
| 4475 | if( nuspan==ULONG_MAX ){ |
| 4476 | pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1; |
| 4477 | }else if( pss->iStep>LLONG_MIN ){ |
| 4478 | pss->uSeqIndexMax = nuspan/-pss->iStep; |
| 4479 | } |
| 4480 | } |
| 4481 | }else if( pss->iTerm > pss->iBase ){ |
| 4482 | sqlite3_uint64 puspan = 0; |
| 4483 | if( bSameSigns ){ |
| 4484 | puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase); |
| 4485 | }else{ |
| 4486 | /* Under UBSAN (or on 1's complement machines), must do this in steps. |
| 4487 | * In this clause, iTerm>=0 and iBase<0 . */ |
| 4488 | puspan = 1; |
| 4489 | puspan += pss->iTerm; |
| 4490 | puspan += -(pss->iBase+1); |
| 4491 | } |
| 4492 | if( pss->iStep>0 ){ |
| 4493 | pss->isNotEOF = 1; |
| 4494 | pss->uSeqIndexMax = puspan/pss->iStep; |
| 4495 | } |
| 4496 | }else if( pss->iTerm == pss->iBase ){ |
| 4497 | pss->isNotEOF = 1; |
| 4498 | pss->uSeqIndexMax = 0; |
| 4499 | } |
| 4500 | pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0; |
| 4501 | pss->iValueNow = (pss->isReversing) |
| 4502 | ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax) |
| 4503 | : pss->iBase; |
| 4504 | } |
| 4505 | |
| 4506 | /* |
| 4507 | ** Progress sequence generator to yield next value, if any. |
| 4508 | ** Leave its state to either yield next value or be at EOF. |
| 4509 | ** Return whether there is a next value, or 0 at EOF. |
| 4510 | */ |
| 4511 | static int progressSequence( SequenceSpec *pss ){ |
| 4512 | if( !pss->isNotEOF ) return 0; |
| 4513 | if( pss->isReversing ){ |
| 4514 | if( pss->uSeqIndexNow > 0 ){ |
| 4515 | pss->uSeqIndexNow--; |
| 4516 | pss->iValueNow -= pss->iStep; |
| 4517 | }else{ |
| 4518 | pss->isNotEOF = 0; |
| 4519 | } |
| 4520 | }else{ |
| 4521 | if( pss->uSeqIndexNow < pss->uSeqIndexMax ){ |
| 4522 | pss->uSeqIndexNow++; |
| 4523 | pss->iValueNow += pss->iStep; |
| 4524 | }else{ |
| 4525 | pss->isNotEOF = 0; |
| 4526 | } |
| 4527 | } |
| 4528 | return pss->isNotEOF; |
| 4529 | } |
| 4530 | |
| 4531 | /* series_cursor is a subclass of sqlite3_vtab_cursor which will |
| 4532 | ** serve as the underlying representation of a cursor that scans |
| 4533 | ** over rows of the result |
| 4534 | */ |
| 4535 | typedef struct series_cursor series_cursor; |
| 4536 | struct series_cursor { |
| 4537 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 4538 | SequenceSpec ss; /* (this) Derived class data */ |
| 4539 | }; |
| 4540 | |
| 4541 | /* |
| 4542 | ** The seriesConnect() method is invoked to create a new |
| 4543 | ** series_vtab that describes the generate_series virtual table. |
| @@ -4285,16 +4615,11 @@ | |
| 4615 | /* |
| 4616 | ** Advance a series_cursor to its next row of output. |
| 4617 | */ |
| 4618 | static int seriesNext(sqlite3_vtab_cursor *cur){ |
| 4619 | series_cursor *pCur = (series_cursor*)cur; |
| 4620 | progressSequence( & pCur->ss ); |
| 4621 | return SQLITE_OK; |
| 4622 | } |
| 4623 | |
| 4624 | /* |
| 4625 | ** Return values of columns for the row at which the series_cursor |
| @@ -4306,44 +4631,40 @@ | |
| 4631 | int i /* Which column to return */ |
| 4632 | ){ |
| 4633 | series_cursor *pCur = (series_cursor*)cur; |
| 4634 | sqlite3_int64 x = 0; |
| 4635 | switch( i ){ |
| 4636 | case SERIES_COLUMN_START: x = pCur->ss.iBase; break; |
| 4637 | case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break; |
| 4638 | case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break; |
| 4639 | default: x = pCur->ss.iValueNow; break; |
| 4640 | } |
| 4641 | sqlite3_result_int64(ctx, x); |
| 4642 | return SQLITE_OK; |
| 4643 | } |
| 4644 | |
| 4645 | /* |
| 4646 | ** Return the rowid for the current row, logically equivalent to n+1 where |
| 4647 | ** "n" is the ascending integer in the aforesaid production definition. |
| 4648 | */ |
| 4649 | static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
| 4650 | series_cursor *pCur = (series_cursor*)cur; |
| 4651 | sqlite3_uint64 n = pCur->ss.uSeqIndexNow; |
| 4652 | *pRowid = (sqlite3_int64)((n<0xffffffffffffffff)? n+1 : 0); |
| 4653 | return SQLITE_OK; |
| 4654 | } |
| 4655 | |
| 4656 | /* |
| 4657 | ** Return TRUE if the cursor has been moved off of the last |
| 4658 | ** row of output. |
| 4659 | */ |
| 4660 | static int seriesEof(sqlite3_vtab_cursor *cur){ |
| 4661 | series_cursor *pCur = (series_cursor*)cur; |
| 4662 | return !pCur->ss.isNotEOF; |
| 4663 | } |
| 4664 | |
| 4665 | /* True to cause run-time checking of the start=, stop=, and/or step= |
| 4666 | ** parameters. The only reason to do this is for testing the |
| 4667 | ** constraint checking logic for virtual tables in the SQLite core. |
| 4668 | */ |
| 4669 | #ifndef SQLITE_SERIES_CONSTRAINT_VERIFY |
| 4670 | # define SQLITE_SERIES_CONSTRAINT_VERIFY 0 |
| @@ -4350,11 +4671,11 @@ | |
| 4671 | #endif |
| 4672 | |
| 4673 | /* |
| 4674 | ** This method is called to "rewind" the series_cursor object back |
| 4675 | ** to the first row of output. This method is always called at least |
| 4676 | ** once prior to any call to seriesColumn() or seriesRowid() or |
| 4677 | ** seriesEof(). |
| 4678 | ** |
| 4679 | ** The query plan selected by seriesBestIndex is passed in the idxNum |
| 4680 | ** parameter. (idxStr is not used in this implementation.) idxNum |
| 4681 | ** is a bitmask showing which constraints are available: |
| @@ -4370,58 +4691,53 @@ | |
| 4691 | ** This routine should initialize the cursor and position it so that it |
| 4692 | ** is pointing at the first row, or pointing off the end of the table |
| 4693 | ** (so that seriesEof() will return true) if the table is empty. |
| 4694 | */ |
| 4695 | static int seriesFilter( |
| 4696 | sqlite3_vtab_cursor *pVtabCursor, |
| 4697 | int idxNum, const char *idxStrUnused, |
| 4698 | int argc, sqlite3_value **argv |
| 4699 | ){ |
| 4700 | series_cursor *pCur = (series_cursor *)pVtabCursor; |
| 4701 | int i = 0; |
| 4702 | (void)idxStrUnused; |
| 4703 | if( idxNum & 1 ){ |
| 4704 | pCur->ss.iBase = sqlite3_value_int64(argv[i++]); |
| 4705 | }else{ |
| 4706 | pCur->ss.iBase = 0; |
| 4707 | } |
| 4708 | if( idxNum & 2 ){ |
| 4709 | pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); |
| 4710 | }else{ |
| 4711 | pCur->ss.iTerm = 0xffffffff; |
| 4712 | } |
| 4713 | if( idxNum & 4 ){ |
| 4714 | pCur->ss.iStep = sqlite3_value_int64(argv[i++]); |
| 4715 | if( pCur->ss.iStep==0 ){ |
| 4716 | pCur->ss.iStep = 1; |
| 4717 | }else if( pCur->ss.iStep<0 ){ |
| 4718 | if( (idxNum & 16)==0 ) idxNum |= 8; |
| 4719 | } |
| 4720 | }else{ |
| 4721 | pCur->ss.iStep = 1; |
| 4722 | } |
| 4723 | for(i=0; i<argc; i++){ |
| 4724 | if( sqlite3_value_type(argv[i])==SQLITE_NULL ){ |
| 4725 | /* If any of the constraints have a NULL value, then return no rows. |
| 4726 | ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */ |
| 4727 | pCur->ss.iBase = 1; |
| 4728 | pCur->ss.iTerm = 0; |
| 4729 | pCur->ss.iStep = 1; |
| 4730 | break; |
| 4731 | } |
| 4732 | } |
| 4733 | if( idxNum & 8 ){ |
| 4734 | pCur->ss.isReversing = pCur->ss.iStep > 0; |
| 4735 | }else{ |
| 4736 | pCur->ss.isReversing = pCur->ss.iStep < 0; |
| 4737 | } |
| 4738 | setupSequence( &pCur->ss ); |
| 4739 | return SQLITE_OK; |
| 4740 | } |
| 4741 | |
| 4742 | /* |
| 4743 | ** SQLite will invoke this method one or more times while planning a query |
| @@ -5172,11 +5488,11 @@ | |
| 5488 | re_append(p, RE_OP_FORK, -sz); |
| 5489 | } |
| 5490 | break; |
| 5491 | } |
| 5492 | case '[': { |
| 5493 | unsigned int iFirst = p->nState; |
| 5494 | if( rePeek(p)=='^' ){ |
| 5495 | re_append(p, RE_OP_CC_EXC, 0); |
| 5496 | p->sIn.i++; |
| 5497 | }else{ |
| 5498 | re_append(p, RE_OP_CC_INC, 0); |
| @@ -5196,11 +5512,11 @@ | |
| 5512 | re_append(p, RE_OP_CC_VALUE, c); |
| 5513 | } |
| 5514 | if( rePeek(p)==']' ){ p->sIn.i++; break; } |
| 5515 | } |
| 5516 | if( c==0 ) return "unclosed '['"; |
| 5517 | if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst; |
| 5518 | break; |
| 5519 | } |
| 5520 | case '\\': { |
| 5521 | int specialOp = 0; |
| 5522 | switch( rePeek(p) ){ |
| @@ -9213,13 +9529,23 @@ | |
| 9529 | ** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry |
| 9530 | ** linked list. Remove it from the list and free the object. |
| 9531 | */ |
| 9532 | static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ |
| 9533 | if( pOld ){ |
| 9534 | if( pTab->pFirstEntry==pOld ){ |
| 9535 | pTab->pFirstEntry = pOld->pNext; |
| 9536 | if( pTab->pLastEntry==pOld ) pTab->pLastEntry = 0; |
| 9537 | }else{ |
| 9538 | ZipfileEntry *p; |
| 9539 | for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 9540 | if( p->pNext==pOld ){ |
| 9541 | p->pNext = pOld->pNext; |
| 9542 | if( pTab->pLastEntry==pOld ) pTab->pLastEntry = p; |
| 9543 | break; |
| 9544 | } |
| 9545 | } |
| 9546 | } |
| 9547 | zipfileEntryFree(pOld); |
| 9548 | } |
| 9549 | } |
| 9550 | |
| 9551 | /* |
| @@ -12510,10 +12836,11 @@ | |
| 12836 | #endif |
| 12837 | |
| 12838 | #endif /* ifndef _SQLITE_RECOVER_H */ |
| 12839 | |
| 12840 | /************************* End ../ext/recover/sqlite3recover.h ********************/ |
| 12841 | # ifndef SQLITE_HAVE_SQLITE3R |
| 12842 | /************************* Begin ../ext/recover/dbdata.c ******************/ |
| 12843 | /* |
| 12844 | ** 2019-04-17 |
| 12845 | ** |
| 12846 | ** The author disclaims copyright to this source code. In place of |
| @@ -14576,11 +14903,11 @@ | |
| 14903 | pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); |
| 14904 | while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 14905 | int iField = sqlite3_column_int(pStmt, 0); |
| 14906 | int iCol = sqlite3_column_int(pStmt, 1); |
| 14907 | |
| 14908 | assert( iCol<pNew->nCol ); |
| 14909 | pNew->aCol[iCol].iField = iField; |
| 14910 | |
| 14911 | pNew->bIntkey = 0; |
| 14912 | iPk = -2; |
| 14913 | } |
| @@ -16340,10 +16667,11 @@ | |
| 16667 | } |
| 16668 | |
| 16669 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 16670 | |
| 16671 | /************************* End ../ext/recover/sqlite3recover.c ********************/ |
| 16672 | # endif /* SQLITE_HAVE_SQLITE3R */ |
| 16673 | #endif |
| 16674 | #ifdef SQLITE_SHELL_EXTSRC |
| 16675 | # include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC) |
| 16676 | #endif |
| 16677 | |
| @@ -16521,10 +16849,11 @@ | |
| 16849 | #define SHFLG_CountChanges 0x00000020 /* .changes setting */ |
| 16850 | #define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */ |
| 16851 | #define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ |
| 16852 | #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ |
| 16853 | #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ |
| 16854 | #define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */ |
| 16855 | |
| 16856 | /* |
| 16857 | ** Macros for testing and setting shellFlgs |
| 16858 | */ |
| 16859 | #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) |
| @@ -16764,10 +17093,11 @@ | |
| 17093 | if( hasCRNL ){ |
| 17094 | /* If the original contains \r\n then do no conversions back to \n */ |
| 17095 | }else{ |
| 17096 | /* If the file did not originally contain \r\n then convert any new |
| 17097 | ** \r\n back into \n */ |
| 17098 | p[sz] = 0; |
| 17099 | for(i=j=0; i<sz; i++){ |
| 17100 | if( p[i]=='\r' && p[i+1]=='\n' ) i++; |
| 17101 | p[j++] = p[i]; |
| 17102 | } |
| 17103 | sz = j; |
| @@ -16854,10 +17184,11 @@ | |
| 17184 | */ |
| 17185 | static void output_quoted_string(FILE *out, const char *z){ |
| 17186 | int i; |
| 17187 | char c; |
| 17188 | setBinaryMode(out, 1); |
| 17189 | if( z==0 ) return; |
| 17190 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 17191 | if( c==0 ){ |
| 17192 | utf8_printf(out,"'%s'",z); |
| 17193 | }else{ |
| 17194 | raw_printf(out, "'"); |
| @@ -16983,10 +17314,11 @@ | |
| 17314 | /* |
| 17315 | ** Output the given string as a quoted according to JSON quoting rules. |
| 17316 | */ |
| 17317 | static void output_json_string(FILE *out, const char *z, i64 n){ |
| 17318 | unsigned int c; |
| 17319 | if( z==0 ) z = ""; |
| 17320 | if( n<0 ) n = strlen(z); |
| 17321 | fputc('"', out); |
| 17322 | while( n-- ){ |
| 17323 | c = *(z++); |
| 17324 | if( c=='\\' || c=='"' ){ |
| @@ -17107,12 +17439,11 @@ | |
| 17439 | /* |
| 17440 | ** This routine runs when the user presses Ctrl-C |
| 17441 | */ |
| 17442 | static void interrupt_handler(int NotUsed){ |
| 17443 | UNUSED_PARAMETER(NotUsed); |
| 17444 | if( ++seenInterrupt>1 ) exit(1); |
| 17445 | if( globalDb ) sqlite3_interrupt(globalDb); |
| 17446 | } |
| 17447 | |
| 17448 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 17449 | /* |
| @@ -18559,14 +18890,18 @@ | |
| 18890 | zVar = zNum; |
| 18891 | } |
| 18892 | sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC); |
| 18893 | if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){ |
| 18894 | sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0)); |
| 18895 | #ifdef NAN |
| 18896 | }else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){ |
| 18897 | sqlite3_bind_double(pStmt, i, NAN); |
| 18898 | #endif |
| 18899 | #ifdef INFINITY |
| 18900 | }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ |
| 18901 | sqlite3_bind_double(pStmt, i, INFINITY); |
| 18902 | #endif |
| 18903 | }else{ |
| 18904 | sqlite3_bind_null(pStmt, i); |
| 18905 | } |
| 18906 | sqlite3_reset(pQ); |
| 18907 | } |
| @@ -18806,11 +19141,11 @@ | |
| 19141 | nAlloc = nColumn*4; |
| 19142 | if( nAlloc<=0 ) nAlloc = 1; |
| 19143 | azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); |
| 19144 | shell_check_oom(azData); |
| 19145 | azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 19146 | shell_check_oom(azNextLine); |
| 19147 | memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
| 19148 | if( p->cmOpts.bQuote ){ |
| 19149 | azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); |
| 19150 | shell_check_oom(azQuoted); |
| 19151 | memset(azQuoted, 0, nColumn*sizeof(char*) ); |
| @@ -18836,10 +19171,11 @@ | |
| 19171 | if( wx==0 ){ |
| 19172 | wx = p->cmOpts.iWrap; |
| 19173 | } |
| 19174 | if( wx<0 ) wx = -wx; |
| 19175 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 19176 | if( uz==0 ) uz = (u8*)""; |
| 19177 | azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); |
| 19178 | } |
| 19179 | do{ |
| 19180 | int useNextLine = bNextLine; |
| 19181 | bNextLine = 0; |
| @@ -19769,17 +20105,17 @@ | |
| 20105 | " from the \".mode\" output mode", |
| 20106 | " * If FILE begins with \"|\" then it is a command that generates the", |
| 20107 | " input text.", |
| 20108 | #endif |
| 20109 | #ifndef SQLITE_OMIT_TEST_CONTROL |
| 20110 | ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", |
| 20111 | #endif |
| 20112 | ".indexes ?TABLE? Show names of indexes", |
| 20113 | " If TABLE is specified, only show indexes for", |
| 20114 | " tables matching TABLE using the LIKE operator.", |
| 20115 | #ifdef SQLITE_ENABLE_IOTRACE |
| 20116 | ",iotrace FILE Enable I/O diagnostic logging to FILE", |
| 20117 | #endif |
| 20118 | ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", |
| 20119 | ".lint OPTIONS Report potential schema issues.", |
| 20120 | " Options:", |
| 20121 | " fkey-indexes Find missing foreign key indexes", |
| @@ -19884,11 +20220,11 @@ | |
| 20220 | ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", |
| 20221 | ".schema ?PATTERN? Show the CREATE statements matching PATTERN", |
| 20222 | " Options:", |
| 20223 | " --indent Try to pretty-print the schema", |
| 20224 | " --nosys Omit objects whose names start with \"sqlite_\"", |
| 20225 | ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", |
| 20226 | " Options:", |
| 20227 | " --init Create a new SELFTEST table", |
| 20228 | " -v Verbose output", |
| 20229 | ".separator COL ?ROW? Change the column and row separators", |
| 20230 | #if defined(SQLITE_ENABLE_SESSION) |
| @@ -19926,13 +20262,13 @@ | |
| 20262 | #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) |
| 20263 | ".system CMD ARGS... Run CMD ARGS... in a system shell", |
| 20264 | #endif |
| 20265 | ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", |
| 20266 | #ifndef SQLITE_SHELL_FIDDLE |
| 20267 | ",testcase NAME Begin redirecting output to 'testcase-out.txt'", |
| 20268 | #endif |
| 20269 | ",testctrl CMD ... Run various sqlite3_test_control() operations", |
| 20270 | " Run \".testctrl\" with no arguments for details", |
| 20271 | ".timeout MS Try opening locked tables for MS milliseconds", |
| 20272 | ".timer on|off Turn SQL timer on or off", |
| 20273 | #ifndef SQLITE_OMIT_TRACE |
| 20274 | ".trace ?OPTIONS? Output each SQL statement as it is run", |
| @@ -19980,20 +20316,45 @@ | |
| 20316 | || zPattern[0]=='0' |
| 20317 | || cli_strcmp(zPattern,"-a")==0 |
| 20318 | || cli_strcmp(zPattern,"-all")==0 |
| 20319 | || cli_strcmp(zPattern,"--all")==0 |
| 20320 | ){ |
| 20321 | enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 }; |
| 20322 | enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 }; |
| 20323 | /* Show all or most commands |
| 20324 | ** *zPattern==0 => summary of documented commands only |
| 20325 | ** *zPattern=='0' => whole help for undocumented commands |
| 20326 | ** Otherwise => whole help for documented commands |
| 20327 | */ |
| 20328 | enum HelpWanted hw = HW_SummaryOnly; |
| 20329 | enum HelpHave hh = HH_More; |
| 20330 | if( zPattern!=0 ){ |
| 20331 | hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull; |
| 20332 | } |
| 20333 | for(i=0; i<ArraySize(azHelp); i++){ |
| 20334 | switch( azHelp[i][0] ){ |
| 20335 | case ',': |
| 20336 | hh = HH_Summary|HH_Undoc; |
| 20337 | break; |
| 20338 | case '.': |
| 20339 | hh = HH_Summary; |
| 20340 | break; |
| 20341 | default: |
| 20342 | hh &= ~HH_Summary; |
| 20343 | break; |
| 20344 | } |
| 20345 | if( ((hw^hh)&HH_Undoc)==0 ){ |
| 20346 | if( (hh&HH_Summary)!=0 ){ |
| 20347 | utf8_printf(out, ".%s\n", azHelp[i]+1); |
| 20348 | ++n; |
| 20349 | }else if( (hw&HW_SummaryOnly)==0 ){ |
| 20350 | utf8_printf(out, "%s\n", azHelp[i]); |
| 20351 | } |
| 20352 | } |
| 20353 | } |
| 20354 | }else{ |
| 20355 | /* Seek documented commands for which zPattern is an exact prefix */ |
| 20356 | zPat = sqlite3_mprintf(".%s*", zPattern); |
| 20357 | shell_check_oom(zPat); |
| 20358 | for(i=0; i<ArraySize(azHelp); i++){ |
| 20359 | if( sqlite3_strglob(zPat, azHelp[i])==0 ){ |
| 20360 | utf8_printf(out, "%s\n", azHelp[i]); |
| @@ -20002,28 +20363,32 @@ | |
| 20363 | } |
| 20364 | } |
| 20365 | sqlite3_free(zPat); |
| 20366 | if( n ){ |
| 20367 | if( n==1 ){ |
| 20368 | /* when zPattern is a prefix of exactly one command, then include |
| 20369 | ** the details of that command, which should begin at offset j */ |
| 20370 | while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
| 20371 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20372 | j++; |
| 20373 | } |
| 20374 | } |
| 20375 | return n; |
| 20376 | } |
| 20377 | /* Look for documented commands that contain zPattern anywhere. |
| 20378 | ** Show complete text of all documented commands that match. */ |
| 20379 | zPat = sqlite3_mprintf("%%%s%%", zPattern); |
| 20380 | shell_check_oom(zPat); |
| 20381 | for(i=0; i<ArraySize(azHelp); i++){ |
| 20382 | if( azHelp[i][0]==',' ){ |
| 20383 | while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; |
| 20384 | continue; |
| 20385 | } |
| 20386 | if( azHelp[i][0]=='.' ) j = i; |
| 20387 | if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ |
| 20388 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20389 | while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ |
| 20390 | j++; |
| 20391 | utf8_printf(out, "%s\n", azHelp[j]); |
| 20392 | } |
| 20393 | i = j; |
| 20394 | n++; |
| @@ -20337,17 +20702,34 @@ | |
| 20702 | } |
| 20703 | globalDb = p->db; |
| 20704 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 20705 | utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", |
| 20706 | zDbFilename, sqlite3_errmsg(p->db)); |
| 20707 | if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ |
| 20708 | exit(1); |
| 20709 | } |
| 20710 | sqlite3_close(p->db); |
| 20711 | sqlite3_open(":memory:", &p->db); |
| 20712 | if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ |
| 20713 | utf8_printf(stderr, |
| 20714 | "Also: unable to open substitute in-memory database.\n" |
| 20715 | ); |
| 20716 | exit(1); |
| 20717 | }else{ |
| 20718 | utf8_printf(stderr, |
| 20719 | "Notice: using substitute in-memory database instead of \"%s\"\n", |
| 20720 | zDbFilename); |
| 20721 | } |
| 20722 | } |
| 20723 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); |
| 20724 | |
| 20725 | /* Reflect the use or absence of --unsafe-testing invocation. */ |
| 20726 | { |
| 20727 | int testmode_on = ShellHasFlag(p,SHFLG_TestingMode); |
| 20728 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0); |
| 20729 | sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0); |
| 20730 | } |
| 20731 | |
| 20732 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 20733 | sqlite3_enable_load_extension(p->db, 1); |
| 20734 | #endif |
| 20735 | sqlite3_shathree_init(p->db, 0, 0); |
| @@ -20356,13 +20738,10 @@ | |
| 20738 | sqlite3_base64_init(p->db, 0, 0); |
| 20739 | sqlite3_base85_init(p->db, 0, 0); |
| 20740 | sqlite3_regexp_init(p->db, 0, 0); |
| 20741 | sqlite3_ieee_init(p->db, 0, 0); |
| 20742 | sqlite3_series_init(p->db, 0, 0); |
| 20743 | #ifndef SQLITE_SHELL_FIDDLE |
| 20744 | sqlite3_fileio_init(p->db, 0, 0); |
| 20745 | sqlite3_completion_init(p->db, 0, 0); |
| 20746 | #endif |
| 20747 | #ifdef SQLITE_HAVE_ZLIB |
| @@ -20550,10 +20929,11 @@ | |
| 20929 | ** \s -> space |
| 20930 | ** \" -> " |
| 20931 | ** \' -> ' |
| 20932 | ** \\ -> backslash |
| 20933 | ** \NNN -> ascii character NNN in octal |
| 20934 | ** \xHH -> ascii character HH in hexadecimal |
| 20935 | */ |
| 20936 | static void resolve_backslashes(char *z){ |
| 20937 | int i, j; |
| 20938 | char c; |
| 20939 | while( *z && *z!='\\' ) z++; |
| @@ -20578,10 +20958,19 @@ | |
| 20958 | c = '"'; |
| 20959 | }else if( c=='\'' ){ |
| 20960 | c = '\''; |
| 20961 | }else if( c=='\\' ){ |
| 20962 | c = '\\'; |
| 20963 | }else if( c=='x' ){ |
| 20964 | int nhd = 0, hdv; |
| 20965 | u8 hv = 0; |
| 20966 | while( nhd<2 && (c=z[i+1+nhd])!=0 && (hdv=hexDigitValue(c))>=0 ){ |
| 20967 | hv = (u8)((hv<<4)|hdv); |
| 20968 | ++nhd; |
| 20969 | } |
| 20970 | i += nhd; |
| 20971 | c = (u8)hv; |
| 20972 | }else if( c>='0' && c<='7' ){ |
| 20973 | c -= '0'; |
| 20974 | if( z[i+1]>='0' && z[i+1]<='7' ){ |
| 20975 | i++; |
| 20976 | c = (c<<3) + z[i] - '0'; |
| @@ -20677,11 +21066,11 @@ | |
| 21066 | if( p->traceOut==0 ) return 0; |
| 21067 | if( mType==SQLITE_TRACE_CLOSE ){ |
| 21068 | utf8_printf(p->traceOut, "-- closing database connection\n"); |
| 21069 | return 0; |
| 21070 | } |
| 21071 | if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){ |
| 21072 | zSql = (const char*)pX; |
| 21073 | }else{ |
| 21074 | pStmt = (sqlite3_stmt*)pP; |
| 21075 | switch( p->eTraceType ){ |
| 21076 | case SHELL_TRACE_EXPANDED: { |
| @@ -20709,11 +21098,11 @@ | |
| 21098 | case SQLITE_TRACE_STMT: { |
| 21099 | utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
| 21100 | break; |
| 21101 | } |
| 21102 | case SQLITE_TRACE_PROFILE: { |
| 21103 | sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; |
| 21104 | utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); |
| 21105 | break; |
| 21106 | } |
| 21107 | } |
| 21108 | return 0; |
| @@ -20785,12 +21174,12 @@ | |
| 21174 | ** EOF on end-of-file. |
| 21175 | ** + Report syntax errors on stderr |
| 21176 | */ |
| 21177 | static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ |
| 21178 | int c; |
| 21179 | int cSep = (u8)p->cColSep; |
| 21180 | int rSep = (u8)p->cRowSep; |
| 21181 | p->n = 0; |
| 21182 | c = fgetc(p->in); |
| 21183 | if( c==EOF || seenInterrupt ){ |
| 21184 | p->cTerm = EOF; |
| 21185 | return 0; |
| @@ -20875,12 +21264,12 @@ | |
| 21264 | ** EOF on end-of-file. |
| 21265 | ** + Report syntax errors on stderr |
| 21266 | */ |
| 21267 | static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ |
| 21268 | int c; |
| 21269 | int cSep = (u8)p->cColSep; |
| 21270 | int rSep = (u8)p->cRowSep; |
| 21271 | p->n = 0; |
| 21272 | c = fgetc(p->in); |
| 21273 | if( c==EOF || seenInterrupt ){ |
| 21274 | p->cTerm = EOF; |
| 21275 | return 0; |
| @@ -21070,11 +21459,11 @@ | |
| 21459 | } |
| 21460 | while( sqlite3_step(pQuery)==SQLITE_ROW ){ |
| 21461 | zName = sqlite3_column_text(pQuery, 0); |
| 21462 | zSql = sqlite3_column_text(pQuery, 1); |
| 21463 | if( zName==0 || zSql==0 ) continue; |
| 21464 | if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; |
| 21465 | printf("%s... ", zName); fflush(stdout); |
| 21466 | sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); |
| 21467 | if( zErrMsg ){ |
| 21468 | utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
| 21469 | sqlite3_free(zErrMsg); |
| @@ -21237,11 +21626,13 @@ | |
| 21626 | } |
| 21627 | sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC); |
| 21628 | if( sqlite3_step(pStmt)==SQLITE_ROW |
| 21629 | && sqlite3_column_bytes(pStmt,0)>100 |
| 21630 | ){ |
| 21631 | const u8 *pb = sqlite3_column_blob(pStmt,0); |
| 21632 | shell_check_oom(pb); |
| 21633 | memcpy(aHdr, pb, 100); |
| 21634 | sqlite3_finalize(pStmt); |
| 21635 | }else{ |
| 21636 | raw_printf(stderr, "unable to read database header\n"); |
| 21637 | sqlite3_finalize(pStmt); |
| 21638 | return 1; |
| @@ -22054,11 +22445,14 @@ | |
| 22445 | if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR; |
| 22446 | } |
| 22447 | } |
| 22448 | } |
| 22449 | } |
| 22450 | if( pAr->eCmd==0 ){ |
| 22451 | utf8_printf(stderr, "Required argument missing. Usage:\n"); |
| 22452 | return arUsage(stderr); |
| 22453 | } |
| 22454 | return SQLITE_OK; |
| 22455 | } |
| 22456 | |
| 22457 | /* |
| 22458 | ** This function assumes that all arguments within the ArCommand.azArg[] |
| @@ -22820,11 +23214,11 @@ | |
| 23214 | rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0); |
| 23215 | rc_err_oom_die(rc); |
| 23216 | sqlite3_bind_int(pStmt, 1, nDigits); |
| 23217 | rc = sqlite3_step(pStmt); |
| 23218 | sqlite3_finalize(pStmt); |
| 23219 | if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); |
| 23220 | } |
| 23221 | assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ |
| 23222 | rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); |
| 23223 | rc_err_oom_die(rc); |
| 23224 | rc = sqlite3_step(pStmt); |
| @@ -23752,12 +24146,12 @@ | |
| 24146 | if( nSep>1 ){ |
| 24147 | raw_printf(stderr, "Error: multi-character row separators not allowed" |
| 24148 | " for import\n"); |
| 24149 | goto meta_command_exit; |
| 24150 | } |
| 24151 | sCtx.cColSep = (u8)p->colSeparator[0]; |
| 24152 | sCtx.cRowSep = (u8)p->rowSeparator[0]; |
| 24153 | } |
| 24154 | sCtx.zFile = zFile; |
| 24155 | sCtx.nLine = 1; |
| 24156 | if( sCtx.zFile[0]=='|' ){ |
| 24157 | #ifdef SQLITE_OMIT_POPEN |
| @@ -23949,10 +24343,16 @@ | |
| 24343 | sqlite3_stmt *pStmt; |
| 24344 | int tnum = 0; |
| 24345 | int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ |
| 24346 | int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ |
| 24347 | int i; |
| 24348 | if( !ShellHasFlag(p,SHFLG_TestingMode) ){ |
| 24349 | utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n", |
| 24350 | "imposter"); |
| 24351 | rc = 1; |
| 24352 | goto meta_command_exit; |
| 24353 | } |
| 24354 | if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ |
| 24355 | utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" |
| 24356 | " .imposter off\n"); |
| 24357 | /* Also allowed, but not documented: |
| 24358 | ** |
| @@ -24133,11 +24533,12 @@ | |
| 24533 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE) |
| 24534 | if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){ |
| 24535 | const char *zFile, *zProc; |
| 24536 | char *zErrMsg = 0; |
| 24537 | failIfSafeMode(p, "cannot run .load in safe mode"); |
| 24538 | if( nArg<2 || azArg[1][0]==0 ){ |
| 24539 | /* Must have a non-empty FILE. (Will not load self.) */ |
| 24540 | raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); |
| 24541 | rc = 1; |
| 24542 | goto meta_command_exit; |
| 24543 | } |
| 24544 | zFile = azArg[1]; |
| @@ -25457,32 +25858,40 @@ | |
| 25858 | "' OR ') as query, tname from tabcols group by tname)" |
| 25859 | , zRevText); |
| 25860 | shell_check_oom(zRevText); |
| 25861 | if( bDebug ) utf8_printf(p->out, "%s\n", zRevText); |
| 25862 | lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); |
| 25863 | if( lrc!=SQLITE_OK ){ |
| 25864 | /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the |
| 25865 | ** user does cruel and unnatural things like ".limit expr_depth 0". */ |
| 25866 | rc = 1; |
| 25867 | }else{ |
| 25868 | if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC); |
| 25869 | lrc = SQLITE_ROW==sqlite3_step(pStmt); |
| 25870 | if( lrc ){ |
| 25871 | const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); |
| 25872 | sqlite3_stmt *pCheckStmt; |
| 25873 | lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); |
| 25874 | if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery); |
| 25875 | if( lrc!=SQLITE_OK ){ |
| 25876 | rc = 1; |
| 25877 | }else{ |
| 25878 | if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ |
| 25879 | double countIrreversible = sqlite3_column_double(pCheckStmt, 0); |
| 25880 | if( countIrreversible>0 ){ |
| 25881 | int sz = (int)(countIrreversible + 0.5); |
| 25882 | utf8_printf(stderr, |
| 25883 | "Digest includes %d invalidly encoded text field%s.\n", |
| 25884 | sz, (sz>1)? "s": ""); |
| 25885 | } |
| 25886 | } |
| 25887 | sqlite3_finalize(pCheckStmt); |
| 25888 | } |
| 25889 | sqlite3_finalize(pStmt); |
| 25890 | } |
| 25891 | } |
| 25892 | if( rc ) utf8_printf(stderr, ".sha3sum failed.\n"); |
| 25893 | sqlite3_free(zRevText); |
| 25894 | } |
| 25895 | #endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */ |
| 25896 | sqlite3_free(zSql); |
| 25897 | }else |
| @@ -25741,10 +26150,16 @@ | |
| 26150 | int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */ |
| 26151 | int isOk = 0; |
| 26152 | int i, n2; |
| 26153 | const char *zCmd = 0; |
| 26154 | |
| 26155 | if( !ShellHasFlag(p,SHFLG_TestingMode) ){ |
| 26156 | utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n", |
| 26157 | "testctrl"); |
| 26158 | rc = 1; |
| 26159 | goto meta_command_exit; |
| 26160 | } |
| 26161 | open_db(p, 0); |
| 26162 | zCmd = nArg>=2 ? azArg[1] : "help"; |
| 26163 | |
| 26164 | /* The argument can optionally begin with "-" or "--" */ |
| 26165 | if( zCmd[0]=='-' && zCmd[1] ){ |
| @@ -26740,10 +27155,14 @@ | |
| 27155 | " -sorterref SIZE sorter references threshold size\n" |
| 27156 | #endif |
| 27157 | " -stats print memory stats before each finalize\n" |
| 27158 | " -table set output mode to 'table'\n" |
| 27159 | " -tabs set output mode to 'tabs'\n" |
| 27160 | " -unsafe-testing allow unsafe commands and modes for testing\n" |
| 27161 | #if SHELL_WIN_UTF8_OPT |
| 27162 | " -utf8 setup interactive console code page for UTF-8\n" |
| 27163 | #endif |
| 27164 | " -version show SQLite version\n" |
| 27165 | " -vfs NAME use NAME as the default VFS\n" |
| 27166 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 27167 | " -vfstrace enable tracing of all VFS calls\n" |
| 27168 | #endif |
| @@ -26831,10 +27250,14 @@ | |
| 27250 | argv[0], argv[argc-1]); |
| 27251 | exit(1); |
| 27252 | } |
| 27253 | return argv[i]; |
| 27254 | } |
| 27255 | |
| 27256 | static void sayAbnormalExit(void){ |
| 27257 | if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n"); |
| 27258 | } |
| 27259 | |
| 27260 | #ifndef SQLITE_SHELL_IS_UTF8 |
| 27261 | # if (defined(_WIN32) || defined(WIN32)) \ |
| 27262 | && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) |
| 27263 | # define SQLITE_SHELL_IS_UTF8 (0) |
| @@ -26852,11 +27275,11 @@ | |
| 27275 | #else |
| 27276 | int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ |
| 27277 | char **argv; |
| 27278 | #endif |
| 27279 | #ifdef SQLITE_DEBUG |
| 27280 | sqlite3_int64 mem_main_enter = 0; |
| 27281 | #endif |
| 27282 | char *zErrMsg = 0; |
| 27283 | #ifdef SQLITE_SHELL_FIDDLE |
| 27284 | # define data shellState |
| 27285 | #else |
| @@ -26873,22 +27296,27 @@ | |
| 27296 | const char *zVfs = 0; /* Value of -vfs command-line option */ |
| 27297 | #if !SQLITE_SHELL_IS_UTF8 |
| 27298 | char **argvToFree = 0; |
| 27299 | int argcToFree = 0; |
| 27300 | #endif |
| 27301 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 27302 | |
| 27303 | #ifdef SQLITE_SHELL_FIDDLE |
| 27304 | stdin_is_interactive = 0; |
| 27305 | stdout_is_console = 1; |
| 27306 | data.wasm.zDefaultDbName = "/fiddle.sqlite3"; |
| 27307 | #else |
| 27308 | stdin_is_interactive = isatty(0); |
| 27309 | stdout_is_console = isatty(1); |
| 27310 | #endif |
| 27311 | #if SHELL_WIN_UTF8_OPT |
| 27312 | atexit(console_restore); /* Needs revision for CLI as library call */ |
| 27313 | #endif |
| 27314 | atexit(sayAbnormalExit); |
| 27315 | #ifdef SQLITE_DEBUG |
| 27316 | mem_main_enter = sqlite3_memory_used(); |
| 27317 | #endif |
| 27318 | #if !defined(_WIN32_WCE) |
| 27319 | if( getenv("SQLITE_DEBUG_BREAK") ){ |
| 27320 | if( isatty(0) && isatty(2) ){ |
| 27321 | fprintf(stderr, |
| 27322 | "attach debugger to process %d and press any key to continue.\n", |
| @@ -26904,10 +27332,18 @@ | |
| 27332 | #elif defined(SIGTRAP) |
| 27333 | raise(SIGTRAP); |
| 27334 | #endif |
| 27335 | } |
| 27336 | } |
| 27337 | #endif |
| 27338 | /* Register a valid signal handler early, before much else is done. */ |
| 27339 | #ifdef SIGINT |
| 27340 | signal(SIGINT, interrupt_handler); |
| 27341 | #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) |
| 27342 | if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ |
| 27343 | fprintf(stderr, "No ^C handler.\n"); |
| 27344 | } |
| 27345 | #endif |
| 27346 | |
| 27347 | #if USE_SYSTEM_SQLITE+0!=1 |
| 27348 | if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ |
| 27349 | utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| @@ -26944,19 +27380,10 @@ | |
| 27380 | #endif |
| 27381 | |
| 27382 | assert( argc>=1 && argv && argv[0] ); |
| 27383 | Argv0 = argv[0]; |
| 27384 | |
| 27385 | #ifdef SQLITE_SHELL_DBNAME_PROC |
| 27386 | { |
| 27387 | /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name |
| 27388 | ** of a C-function that will provide the name of the database file. Use |
| 27389 | ** this compile-time option to embed this shell program in larger |
| @@ -27109,10 +27536,12 @@ | |
| 27536 | }else if( cli_strcmp(z,"-bail")==0 ){ |
| 27537 | bail_on_error = 1; |
| 27538 | }else if( cli_strcmp(z,"-nonce")==0 ){ |
| 27539 | free(data.zNonce); |
| 27540 | data.zNonce = strdup(argv[++i]); |
| 27541 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 27542 | ShellSetFlag(&data,SHFLG_TestingMode); |
| 27543 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27544 | /* no-op - catch this on the second pass */ |
| 27545 | } |
| 27546 | } |
| 27547 | #ifndef SQLITE_SHELL_FIDDLE |
| @@ -27271,10 +27700,14 @@ | |
| 27700 | return 0; |
| 27701 | }else if( cli_strcmp(z,"-interactive")==0 ){ |
| 27702 | stdin_is_interactive = 1; |
| 27703 | }else if( cli_strcmp(z,"-batch")==0 ){ |
| 27704 | stdin_is_interactive = 0; |
| 27705 | }else if( cli_strcmp(z,"-utf8")==0 ){ |
| 27706 | #if SHELL_WIN_UTF8_OPT |
| 27707 | console_utf8 = 1; |
| 27708 | #endif /* SHELL_WIN_UTF8_OPT */ |
| 27709 | }else if( cli_strcmp(z,"-heap")==0 ){ |
| 27710 | i++; |
| 27711 | }else if( cli_strcmp(z,"-pagecache")==0 ){ |
| 27712 | i+=2; |
| 27713 | }else if( cli_strcmp(z,"-lookaside")==0 ){ |
| @@ -27341,17 +27774,27 @@ | |
| 27774 | readStdin = 0; |
| 27775 | break; |
| 27776 | #endif |
| 27777 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 27778 | data.bSafeMode = data.bSafeModePersist = 1; |
| 27779 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 27780 | /* Acted upon in first pass. */ |
| 27781 | }else{ |
| 27782 | utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
| 27783 | raw_printf(stderr,"Use -help for a list of options.\n"); |
| 27784 | return 1; |
| 27785 | } |
| 27786 | data.cMode = data.mode; |
| 27787 | } |
| 27788 | #if SHELL_WIN_UTF8_OPT |
| 27789 | if( console_utf8 && stdin_is_interactive ){ |
| 27790 | console_prepare(); |
| 27791 | }else{ |
| 27792 | setBinaryMode(stdin, 0); |
| 27793 | console_utf8 = 0; |
| 27794 | } |
| 27795 | #endif |
| 27796 | |
| 27797 | if( !readStdin ){ |
| 27798 | /* Run all arguments that do not begin with '-' as if they were separate |
| 27799 | ** command-line inputs, except for the argToSkip argument which contains |
| 27800 | ** the database filename. |
| 27801 |
+2977
-1365
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -121,10 +121,14 @@ | ||
| 121 | 121 | #if defined(_MSC_VER) && !defined(_WIN64) |
| 122 | 122 | #undef SQLITE_4_BYTE_ALIGNED_MALLOC |
| 123 | 123 | #define SQLITE_4_BYTE_ALIGNED_MALLOC |
| 124 | 124 | #endif /* defined(_MSC_VER) && !defined(_WIN64) */ |
| 125 | 125 | |
| 126 | +#if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 | |
| 127 | +#define HAVE_LOG2 0 | |
| 128 | +#endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */ | |
| 129 | + | |
| 126 | 130 | #endif /* SQLITE_MSVC_H */ |
| 127 | 131 | |
| 128 | 132 | /************** End of msvc.h ************************************************/ |
| 129 | 133 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 130 | 134 | |
| @@ -452,11 +456,11 @@ | ||
| 452 | 456 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 453 | 457 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 454 | 458 | */ |
| 455 | 459 | #define SQLITE_VERSION "3.42.0" |
| 456 | 460 | #define SQLITE_VERSION_NUMBER 3042000 |
| 457 | -#define SQLITE_SOURCE_ID "2023-04-10 18:44:00 4c5a3c5fb351cc1c2ce16c33314ce19c53531f09263f87456283d9d756002f9d" | |
| 461 | +#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" | |
| 458 | 462 | |
| 459 | 463 | /* |
| 460 | 464 | ** CAPI3REF: Run-Time Library Version Numbers |
| 461 | 465 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 462 | 466 | ** |
| @@ -2446,32 +2450,32 @@ | ||
| 2446 | 2450 | ** configuration setting is never used, then the default maximum is determined |
| 2447 | 2451 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2448 | 2452 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2449 | 2453 | ** </dl> |
| 2450 | 2454 | */ |
| 2451 | -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ | |
| 2452 | -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ | |
| 2453 | -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ | |
| 2454 | -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ | |
| 2455 | -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | |
| 2456 | -#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ | |
| 2457 | -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ | |
| 2458 | -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ | |
| 2459 | -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ | |
| 2460 | -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ | |
| 2461 | -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ | |
| 2462 | -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ | |
| 2463 | -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ | |
| 2464 | -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ | |
| 2465 | -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ | |
| 2466 | -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ | |
| 2467 | -#define SQLITE_CONFIG_URI 17 /* int */ | |
| 2468 | -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ | |
| 2469 | -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ | |
| 2455 | +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ | |
| 2456 | +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ | |
| 2457 | +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ | |
| 2458 | +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ | |
| 2459 | +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | |
| 2460 | +#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ | |
| 2461 | +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ | |
| 2462 | +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ | |
| 2463 | +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ | |
| 2464 | +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ | |
| 2465 | +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ | |
| 2466 | +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ | |
| 2467 | +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ | |
| 2468 | +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ | |
| 2469 | +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ | |
| 2470 | +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ | |
| 2471 | +#define SQLITE_CONFIG_URI 17 /* int */ | |
| 2472 | +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ | |
| 2473 | +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ | |
| 2470 | 2474 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2471 | -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ | |
| 2472 | -#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ | |
| 2475 | +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ | |
| 2476 | +#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ | |
| 2473 | 2477 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2474 | 2478 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2475 | 2479 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2476 | 2480 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2477 | 2481 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2702,29 +2706,29 @@ | ||
| 2702 | 2706 | ** additional information. This feature can also be turned on and off |
| 2703 | 2707 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2704 | 2708 | ** </dd> |
| 2705 | 2709 | ** |
| 2706 | 2710 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2707 | -** <dt>SQLITE_DBCONFIG_DQS_DML</td> | |
| 2711 | +** <dt>SQLITE_DBCONFIG_DQS_DML</dt> | |
| 2708 | 2712 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2709 | 2713 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2710 | 2714 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2711 | 2715 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2712 | 2716 | ** compile-time option. |
| 2713 | 2717 | ** </dd> |
| 2714 | 2718 | ** |
| 2715 | 2719 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2716 | -** <dt>SQLITE_DBCONFIG_DQS_DDL</td> | |
| 2720 | +** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> | |
| 2717 | 2721 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2718 | 2722 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2719 | 2723 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2720 | 2724 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2721 | 2725 | ** compile-time option. |
| 2722 | 2726 | ** </dd> |
| 2723 | 2727 | ** |
| 2724 | 2728 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2725 | -** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td> | |
| 2729 | +** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> | |
| 2726 | 2730 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2727 | 2731 | ** assume that database schemas are untainted by malicious content. |
| 2728 | 2732 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2729 | 2733 | ** takes additional defensive steps to protect the application from harm |
| 2730 | 2734 | ** including: |
| @@ -2740,20 +2744,20 @@ | ||
| 2740 | 2744 | ** all applications are advised to turn it off if possible. This setting |
| 2741 | 2745 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2742 | 2746 | ** </dd> |
| 2743 | 2747 | ** |
| 2744 | 2748 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2745 | -** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td> | |
| 2749 | +** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> | |
| 2746 | 2750 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2747 | 2751 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2748 | 2752 | ** created database file to have a schema format version number (the 4-byte |
| 2749 | 2753 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2750 | 2754 | ** means that the resulting database file will be readable and writable by |
| 2751 | 2755 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2752 | 2756 | ** newly created databases are generally not understandable by SQLite versions |
| 2753 | 2757 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2754 | -** is now scarcely any need to generated database files that are compatible | |
| 2758 | +** is now scarcely any need to generate database files that are compatible | |
| 2755 | 2759 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2756 | 2760 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2757 | 2761 | ** ability to generate new database files that are compatible with version |
| 2758 | 2762 | ** 3.0.0. |
| 2759 | 2763 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2762,27 +2766,39 @@ | ||
| 2762 | 2766 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2763 | 2767 | ** either generated columns or decending indexes. |
| 2764 | 2768 | ** </dd> |
| 2765 | 2769 | ** |
| 2766 | 2770 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2767 | -** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td> | |
| 2771 | +** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> | |
| 2768 | 2772 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2769 | 2773 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2770 | 2774 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2771 | 2775 | ** statistics. For statistics to be collected, the flag must be set on |
| 2772 | 2776 | ** the database handle both when the SQL statement is prepared and when it |
| 2773 | 2777 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2774 | -** by default.</dd> | |
| 2778 | +** by default. This option takes two arguments: an integer and a pointer to | |
| 2779 | +** an integer.. The first argument is 1, 0, or -1 to enable, disable, or | |
| 2780 | +** leave unchanged the statement scanstatus option. If the second argument | |
| 2781 | +** is not NULL, then the value of the statement scanstatus setting after | |
| 2782 | +** processing the first argument is written into the integer that the second | |
| 2783 | +** argument points to. | |
| 2784 | +** </dd> | |
| 2775 | 2785 | ** |
| 2776 | 2786 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2777 | -** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</td> | |
| 2778 | -** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option change the default order | |
| 2787 | +** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> | |
| 2788 | +** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order | |
| 2779 | 2789 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2780 | 2790 | ** and work toward the beginning rather than starting at the beginning and |
| 2781 | -** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the | |
| 2782 | -** same as setting [PRAGMA reverse_unordered_selects]. This configuration option | |
| 2783 | -** is useful for application testing.</dd> | |
| 2791 | +** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the | |
| 2792 | +** same as setting [PRAGMA reverse_unordered_selects]. This option takes | |
| 2793 | +** two arguments which are an integer and a pointer to an integer. The first | |
| 2794 | +** argument is 1, 0, or -1 to enable, disable, or leave unchanged the | |
| 2795 | +** reverse scan order flag, respectively. If the second argument is not NULL, | |
| 2796 | +** then 0 or 1 is written into the integer that the second argument points to | |
| 2797 | +** depending on if the reverse scan order flag is set after processing the | |
| 2798 | +** first argument. | |
| 2799 | +** </dd> | |
| 2784 | 2800 | ** |
| 2785 | 2801 | ** </dl> |
| 2786 | 2802 | */ |
| 2787 | 2803 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2788 | 2804 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2800,11 +2816,11 @@ | ||
| 2800 | 2816 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2801 | 2817 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2802 | 2818 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2803 | 2819 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2804 | 2820 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2805 | -#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ | |
| 2821 | +#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ | |
| 2806 | 2822 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2807 | 2823 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2808 | 2824 | |
| 2809 | 2825 | /* |
| 2810 | 2826 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6547,10 +6563,17 @@ | ||
| 6547 | 6563 | ** ^SQLite implements this interface by calling the xSleep() |
| 6548 | 6564 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6549 | 6565 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6550 | 6566 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6551 | 6567 | ** in the previous paragraphs. |
| 6568 | +** | |
| 6569 | +** If a negative argument is passed to sqlite3_sleep() the results vary by | |
| 6570 | +** VFS and operating system. Some system treat a negative argument as an | |
| 6571 | +** instruction to sleep forever. Others understand it to mean do not sleep | |
| 6572 | +** at all. ^In SQLite version 3.42.0 and later, a negative | |
| 6573 | +** argument passed into sqlite3_sleep() is changed to zero before it is relayed | |
| 6574 | +** down into the xSleep method of the VFS. | |
| 6552 | 6575 | */ |
| 6553 | 6576 | SQLITE_API int sqlite3_sleep(int); |
| 6554 | 6577 | |
| 6555 | 6578 | /* |
| 6556 | 6579 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -8174,13 +8197,13 @@ | ||
| 8174 | 8197 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 8175 | 8198 | ** previously entered by the same thread. The behavior |
| 8176 | 8199 | ** is undefined if the mutex is not currently entered by the |
| 8177 | 8200 | ** calling thread or is not currently allocated. |
| 8178 | 8201 | ** |
| 8179 | -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or | |
| 8180 | -** sqlite3_mutex_leave() is a NULL pointer, then all three routines | |
| 8181 | -** behave as no-ops. | |
| 8202 | +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), | |
| 8203 | +** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, | |
| 8204 | +** then any of the four routines behaves as a no-op. | |
| 8182 | 8205 | ** |
| 8183 | 8206 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 8184 | 8207 | */ |
| 8185 | 8208 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 8186 | 8209 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -11106,20 +11129,24 @@ | ||
| 11106 | 11129 | ** [sqlite3session_create()] for details. |
| 11107 | 11130 | */ |
| 11108 | 11131 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 11109 | 11132 | |
| 11110 | 11133 | /* |
| 11111 | -** CAPIREF: Conigure a Session Object | |
| 11134 | +** CAPI3REF: Configure a Session Object | |
| 11112 | 11135 | ** METHOD: sqlite3_session |
| 11113 | 11136 | ** |
| 11114 | 11137 | ** This method is used to configure a session object after it has been |
| 11115 | -** created. At present the only valid value for the second parameter is | |
| 11116 | -** [SQLITE_SESSION_OBJCONFIG_SIZE]. | |
| 11138 | +** created. At present the only valid values for the second parameter are | |
| 11139 | +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. | |
| 11117 | 11140 | ** |
| 11118 | -** Arguments for sqlite3session_object_config() | |
| 11141 | +*/ | |
| 11142 | +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); | |
| 11143 | + | |
| 11144 | +/* | |
| 11145 | +** CAPI3REF: Options for sqlite3session_object_config | |
| 11119 | 11146 | ** |
| 11120 | -** The following values may passed as the the 4th parameter to | |
| 11147 | +** The following values may passed as the the 2nd parameter to | |
| 11121 | 11148 | ** sqlite3session_object_config(). |
| 11122 | 11149 | ** |
| 11123 | 11150 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 11124 | 11151 | ** This option is used to set, clear or query the flag that enables |
| 11125 | 11152 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -11131,16 +11158,25 @@ | ||
| 11131 | 11158 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 11132 | 11159 | ** enabled following the current call, or 0 otherwise. |
| 11133 | 11160 | ** |
| 11134 | 11161 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 11135 | 11162 | ** the first table has been attached to the session object. |
| 11163 | +** | |
| 11164 | +** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> | |
| 11165 | +** This option is used to set, clear or query the flag that enables | |
| 11166 | +** collection of data for tables with no explicit PRIMARY KEY. | |
| 11167 | +** | |
| 11168 | +** Normally, tables with no explicit PRIMARY KEY are simply ignored | |
| 11169 | +** by the sessions module. However, if this flag is set, it behaves | |
| 11170 | +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted | |
| 11171 | +** as their leftmost columns. | |
| 11172 | +** | |
| 11173 | +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after | |
| 11174 | +** the first table has been attached to the session object. | |
| 11136 | 11175 | */ |
| 11137 | -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); | |
| 11138 | - | |
| 11139 | -/* | |
| 11140 | -*/ | |
| 11141 | -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 | |
| 11176 | +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 | |
| 11177 | +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 | |
| 11142 | 11178 | |
| 11143 | 11179 | /* |
| 11144 | 11180 | ** CAPI3REF: Enable Or Disable A Session Object |
| 11145 | 11181 | ** METHOD: sqlite3_session |
| 11146 | 11182 | ** |
| @@ -13652,11 +13688,11 @@ | ||
| 13652 | 13688 | # define SQLITE_INLINE __forceinline |
| 13653 | 13689 | #else |
| 13654 | 13690 | # define SQLITE_NOINLINE |
| 13655 | 13691 | # define SQLITE_INLINE |
| 13656 | 13692 | #endif |
| 13657 | -#if defined(SQLITE_COVERAGE_TEST) | |
| 13693 | +#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) | |
| 13658 | 13694 | # undef SQLITE_INLINE |
| 13659 | 13695 | # define SQLITE_INLINE |
| 13660 | 13696 | #endif |
| 13661 | 13697 | |
| 13662 | 13698 | /* |
| @@ -18264,10 +18300,11 @@ | ||
| 18264 | 18300 | unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ |
| 18265 | 18301 | unsigned bHasExpr:1; /* Index contains an expression, either a literal |
| 18266 | 18302 | ** expression, or a reference to a VIRTUAL column */ |
| 18267 | 18303 | #ifdef SQLITE_ENABLE_STAT4 |
| 18268 | 18304 | int nSample; /* Number of elements in aSample[] */ |
| 18305 | + int mxSample; /* Number of slots allocated to aSample[] */ | |
| 18269 | 18306 | int nSampleCol; /* Size of IndexSample.anEq[] and so on */ |
| 18270 | 18307 | tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ |
| 18271 | 18308 | IndexSample *aSample; /* Samples of the left-most key */ |
| 18272 | 18309 | tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ |
| 18273 | 18310 | tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ |
| @@ -20020,19 +20057,23 @@ | ||
| 20020 | 20057 | # define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) |
| 20021 | 20058 | # define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) |
| 20022 | 20059 | # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) |
| 20023 | 20060 | # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) |
| 20024 | 20061 | # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) |
| 20062 | +# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) | |
| 20063 | +# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) | |
| 20025 | 20064 | #else |
| 20026 | 20065 | # define sqlite3Toupper(x) toupper((unsigned char)(x)) |
| 20027 | 20066 | # define sqlite3Isspace(x) isspace((unsigned char)(x)) |
| 20028 | 20067 | # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) |
| 20029 | 20068 | # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) |
| 20030 | 20069 | # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) |
| 20031 | 20070 | # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) |
| 20032 | 20071 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) |
| 20033 | 20072 | # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') |
| 20073 | +# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') | |
| 20074 | +# define sqlite3JsonId2(x) sqlite3IsIdChar(x) | |
| 20034 | 20075 | #endif |
| 20035 | 20076 | SQLITE_PRIVATE int sqlite3IsIdChar(u8); |
| 20036 | 20077 | |
| 20037 | 20078 | /* |
| 20038 | 20079 | ** Internal function prototypes |
| @@ -20465,11 +20506,11 @@ | ||
| 20465 | 20506 | SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); |
| 20466 | 20507 | SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); |
| 20467 | 20508 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); |
| 20468 | 20509 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); |
| 20469 | 20510 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); |
| 20470 | -SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); | |
| 20511 | +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); | |
| 20471 | 20512 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 20472 | 20513 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 20473 | 20514 | #endif |
| 20474 | 20515 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); |
| 20475 | 20516 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| @@ -22161,11 +22202,11 @@ | ||
| 22161 | 22202 | ** isalpha() 0x02 |
| 22162 | 22203 | ** isdigit() 0x04 |
| 22163 | 22204 | ** isalnum() 0x06 |
| 22164 | 22205 | ** isxdigit() 0x08 |
| 22165 | 22206 | ** toupper() 0x20 |
| 22166 | -** SQLite identifier character 0x40 | |
| 22207 | +** SQLite identifier character 0x40 $, _, or non-ascii | |
| 22167 | 22208 | ** Quote character 0x80 |
| 22168 | 22209 | ** |
| 22169 | 22210 | ** Bit 0x20 is set if the mapped character requires translation to upper |
| 22170 | 22211 | ** case. i.e. if the character is a lower-case ASCII character. |
| 22171 | 22212 | ** If x is a lower-case ASCII character, then its upper-case equivalent |
| @@ -23654,10 +23695,11 @@ | ||
| 23654 | 23695 | char validYMD; /* True (1) if Y,M,D are valid */ |
| 23655 | 23696 | char validHMS; /* True (1) if h,m,s are valid */ |
| 23656 | 23697 | char validTZ; /* True (1) if tz is valid */ |
| 23657 | 23698 | char tzSet; /* Timezone was set explicitly */ |
| 23658 | 23699 | char isError; /* An overflow has occurred */ |
| 23700 | + char useSubsec; /* Display subsecond precision */ | |
| 23659 | 23701 | }; |
| 23660 | 23702 | |
| 23661 | 23703 | |
| 23662 | 23704 | /* |
| 23663 | 23705 | ** Convert zDate into one or more integers according to the conversion |
| @@ -23968,10 +24010,15 @@ | ||
| 23968 | 24010 | }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){ |
| 23969 | 24011 | return setDateTimeToCurrent(context, p); |
| 23970 | 24012 | }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ |
| 23971 | 24013 | setRawDateNumber(p, r); |
| 23972 | 24014 | return 0; |
| 24015 | + }else if( (sqlite3StrICmp(zDate,"subsec")==0 | |
| 24016 | + || sqlite3StrICmp(zDate,"subsecond")==0) | |
| 24017 | + && sqlite3NotPureFunc(context) ){ | |
| 24018 | + p->useSubsec = 1; | |
| 24019 | + return setDateTimeToCurrent(context, p); | |
| 23973 | 24020 | } |
| 23974 | 24021 | return 1; |
| 23975 | 24022 | } |
| 23976 | 24023 | |
| 23977 | 24024 | /* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. |
| @@ -24382,12 +24429,26 @@ | ||
| 24382 | 24429 | /* |
| 24383 | 24430 | ** start of TTTTT |
| 24384 | 24431 | ** |
| 24385 | 24432 | ** Move the date backwards to the beginning of the current day, |
| 24386 | 24433 | ** or month or year. |
| 24434 | + ** | |
| 24435 | + ** subsecond | |
| 24436 | + ** subsec | |
| 24437 | + ** | |
| 24438 | + ** Show subsecond precision in the output of datetime() and | |
| 24439 | + ** unixepoch() and strftime('%s'). | |
| 24387 | 24440 | */ |
| 24388 | - if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; | |
| 24441 | + if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ | |
| 24442 | + if( sqlite3_stricmp(z, "subsec")==0 | |
| 24443 | + || sqlite3_stricmp(z, "subsecond")==0 | |
| 24444 | + ){ | |
| 24445 | + p->useSubsec = 1; | |
| 24446 | + rc = 0; | |
| 24447 | + } | |
| 24448 | + break; | |
| 24449 | + } | |
| 24389 | 24450 | if( !p->validJD && !p->validYMD && !p->validHMS ) break; |
| 24390 | 24451 | z += 9; |
| 24391 | 24452 | computeYMD(p); |
| 24392 | 24453 | p->validHMS = 1; |
| 24393 | 24454 | p->h = p->m = 0; |
| @@ -24581,11 +24642,15 @@ | ||
| 24581 | 24642 | sqlite3_value **argv |
| 24582 | 24643 | ){ |
| 24583 | 24644 | DateTime x; |
| 24584 | 24645 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24585 | 24646 | computeJD(&x); |
| 24586 | - sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); | |
| 24647 | + if( x.useSubsec ){ | |
| 24648 | + sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); | |
| 24649 | + }else{ | |
| 24650 | + sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); | |
| 24651 | + } | |
| 24587 | 24652 | } |
| 24588 | 24653 | } |
| 24589 | 24654 | |
| 24590 | 24655 | /* |
| 24591 | 24656 | ** datetime( TIMESTRING, MOD, MOD, ...) |
| @@ -24597,12 +24662,12 @@ | ||
| 24597 | 24662 | int argc, |
| 24598 | 24663 | sqlite3_value **argv |
| 24599 | 24664 | ){ |
| 24600 | 24665 | DateTime x; |
| 24601 | 24666 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24602 | - int Y, s; | |
| 24603 | - char zBuf[24]; | |
| 24667 | + int Y, s, n; | |
| 24668 | + char zBuf[32]; | |
| 24604 | 24669 | computeYMD_HMS(&x); |
| 24605 | 24670 | Y = x.Y; |
| 24606 | 24671 | if( Y<0 ) Y = -Y; |
| 24607 | 24672 | zBuf[1] = '0' + (Y/1000)%10; |
| 24608 | 24673 | zBuf[2] = '0' + (Y/100)%10; |
| @@ -24619,19 +24684,32 @@ | ||
| 24619 | 24684 | zBuf[13] = '0' + (x.h)%10; |
| 24620 | 24685 | zBuf[14] = ':'; |
| 24621 | 24686 | zBuf[15] = '0' + (x.m/10)%10; |
| 24622 | 24687 | zBuf[16] = '0' + (x.m)%10; |
| 24623 | 24688 | zBuf[17] = ':'; |
| 24624 | - s = (int)x.s; | |
| 24625 | - zBuf[18] = '0' + (s/10)%10; | |
| 24626 | - zBuf[19] = '0' + (s)%10; | |
| 24627 | - zBuf[20] = 0; | |
| 24689 | + if( x.useSubsec ){ | |
| 24690 | + s = (int)1000.0*x.s; | |
| 24691 | + zBuf[18] = '0' + (s/10000)%10; | |
| 24692 | + zBuf[19] = '0' + (s/1000)%10; | |
| 24693 | + zBuf[20] = '.'; | |
| 24694 | + zBuf[21] = '0' + (s/100)%10; | |
| 24695 | + zBuf[22] = '0' + (s/10)%10; | |
| 24696 | + zBuf[23] = '0' + (s)%10; | |
| 24697 | + zBuf[24] = 0; | |
| 24698 | + n = 24; | |
| 24699 | + }else{ | |
| 24700 | + s = (int)x.s; | |
| 24701 | + zBuf[18] = '0' + (s/10)%10; | |
| 24702 | + zBuf[19] = '0' + (s)%10; | |
| 24703 | + zBuf[20] = 0; | |
| 24704 | + n = 20; | |
| 24705 | + } | |
| 24628 | 24706 | if( x.Y<0 ){ |
| 24629 | 24707 | zBuf[0] = '-'; |
| 24630 | - sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); | |
| 24708 | + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); | |
| 24631 | 24709 | }else{ |
| 24632 | - sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); | |
| 24710 | + sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); | |
| 24633 | 24711 | } |
| 24634 | 24712 | } |
| 24635 | 24713 | } |
| 24636 | 24714 | |
| 24637 | 24715 | /* |
| @@ -24644,24 +24722,37 @@ | ||
| 24644 | 24722 | int argc, |
| 24645 | 24723 | sqlite3_value **argv |
| 24646 | 24724 | ){ |
| 24647 | 24725 | DateTime x; |
| 24648 | 24726 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24649 | - int s; | |
| 24727 | + int s, n; | |
| 24650 | 24728 | char zBuf[16]; |
| 24651 | 24729 | computeHMS(&x); |
| 24652 | 24730 | zBuf[0] = '0' + (x.h/10)%10; |
| 24653 | 24731 | zBuf[1] = '0' + (x.h)%10; |
| 24654 | 24732 | zBuf[2] = ':'; |
| 24655 | 24733 | zBuf[3] = '0' + (x.m/10)%10; |
| 24656 | 24734 | zBuf[4] = '0' + (x.m)%10; |
| 24657 | 24735 | zBuf[5] = ':'; |
| 24658 | - s = (int)x.s; | |
| 24659 | - zBuf[6] = '0' + (s/10)%10; | |
| 24660 | - zBuf[7] = '0' + (s)%10; | |
| 24661 | - zBuf[8] = 0; | |
| 24662 | - sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); | |
| 24736 | + if( x.useSubsec ){ | |
| 24737 | + s = (int)1000.0*x.s; | |
| 24738 | + zBuf[6] = '0' + (s/10000)%10; | |
| 24739 | + zBuf[7] = '0' + (s/1000)%10; | |
| 24740 | + zBuf[8] = '.'; | |
| 24741 | + zBuf[9] = '0' + (s/100)%10; | |
| 24742 | + zBuf[10] = '0' + (s/10)%10; | |
| 24743 | + zBuf[11] = '0' + (s)%10; | |
| 24744 | + zBuf[12] = 0; | |
| 24745 | + n = 12; | |
| 24746 | + }else{ | |
| 24747 | + s = (int)x.s; | |
| 24748 | + zBuf[6] = '0' + (s/10)%10; | |
| 24749 | + zBuf[7] = '0' + (s)%10; | |
| 24750 | + zBuf[8] = 0; | |
| 24751 | + n = 8; | |
| 24752 | + } | |
| 24753 | + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); | |
| 24663 | 24754 | } |
| 24664 | 24755 | } |
| 24665 | 24756 | |
| 24666 | 24757 | /* |
| 24667 | 24758 | ** date( TIMESTRING, MOD, MOD, ...) |
| @@ -24788,12 +24879,17 @@ | ||
| 24788 | 24879 | case 'M': { |
| 24789 | 24880 | sqlite3_str_appendf(&sRes,"%02d",x.m); |
| 24790 | 24881 | break; |
| 24791 | 24882 | } |
| 24792 | 24883 | case 's': { |
| 24793 | - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); | |
| 24794 | - sqlite3_str_appendf(&sRes,"%lld",iS); | |
| 24884 | + if( x.useSubsec ){ | |
| 24885 | + sqlite3_str_appendf(&sRes,"%.3f", | |
| 24886 | + (x.iJD - 21086676*(i64)10000000)/1000.0); | |
| 24887 | + }else{ | |
| 24888 | + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); | |
| 24889 | + sqlite3_str_appendf(&sRes,"%lld",iS); | |
| 24890 | + } | |
| 24795 | 24891 | break; |
| 24796 | 24892 | } |
| 24797 | 24893 | case 'S': { |
| 24798 | 24894 | sqlite3_str_appendf(&sRes,"%02d",(int)x.s); |
| 24799 | 24895 | break; |
| @@ -30667,10 +30763,11 @@ | ||
| 30667 | 30763 | nsd = 16 + flag_altform2*10; |
| 30668 | 30764 | bufpt = buf; |
| 30669 | 30765 | { |
| 30670 | 30766 | i64 szBufNeeded; /* Size of a temporary buffer needed */ |
| 30671 | 30767 | szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; |
| 30768 | + if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; | |
| 30672 | 30769 | if( szBufNeeded > etBUFSIZE ){ |
| 30673 | 30770 | bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); |
| 30674 | 30771 | if( bufpt==0 ) return; |
| 30675 | 30772 | } |
| 30676 | 30773 | } |
| @@ -30684,14 +30781,16 @@ | ||
| 30684 | 30781 | if( e2<0 ){ |
| 30685 | 30782 | *(bufpt++) = '0'; |
| 30686 | 30783 | }else if( msd>0 ){ |
| 30687 | 30784 | for(; e2>=0; e2--){ |
| 30688 | 30785 | *(bufpt++) = et_getdigit_int(&longvalue,&msd); |
| 30786 | + if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; | |
| 30689 | 30787 | } |
| 30690 | 30788 | }else{ |
| 30691 | 30789 | for(; e2>=0; e2--){ |
| 30692 | 30790 | *(bufpt++) = et_getdigit(&realvalue,&nsd); |
| 30791 | + if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; | |
| 30693 | 30792 | } |
| 30694 | 30793 | } |
| 30695 | 30794 | /* The decimal point */ |
| 30696 | 30795 | if( flag_dp ){ |
| 30697 | 30796 | *(bufpt++) = '.'; |
| @@ -34434,17 +34533,19 @@ | ||
| 34434 | 34533 | }else{ |
| 34435 | 34534 | x = v; |
| 34436 | 34535 | } |
| 34437 | 34536 | i = sizeof(zTemp)-2; |
| 34438 | 34537 | zTemp[sizeof(zTemp)-1] = 0; |
| 34439 | - do{ | |
| 34440 | - zTemp[i--] = (x%10) + '0'; | |
| 34538 | + while( 1 /*exit-by-break*/ ){ | |
| 34539 | + zTemp[i] = (x%10) + '0'; | |
| 34441 | 34540 | x = x/10; |
| 34442 | - }while( x ); | |
| 34443 | - if( v<0 ) zTemp[i--] = '-'; | |
| 34444 | - memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); | |
| 34445 | - return sizeof(zTemp)-2-i; | |
| 34541 | + if( x==0 ) break; | |
| 34542 | + i--; | |
| 34543 | + }; | |
| 34544 | + if( v<0 ) zTemp[--i] = '-'; | |
| 34545 | + memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); | |
| 34546 | + return sizeof(zTemp)-1-i; | |
| 34446 | 34547 | } |
| 34447 | 34548 | |
| 34448 | 34549 | /* |
| 34449 | 34550 | ** Compare the 19-character string zNum against the text representation |
| 34450 | 34551 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| @@ -34605,11 +34706,13 @@ | ||
| 34605 | 34706 | for(i=2; z[i]=='0'; i++){} |
| 34606 | 34707 | for(k=i; sqlite3Isxdigit(z[k]); k++){ |
| 34607 | 34708 | u = u*16 + sqlite3HexToInt(z[k]); |
| 34608 | 34709 | } |
| 34609 | 34710 | memcpy(pOut, &u, 8); |
| 34610 | - return (z[k]==0 && k-i<=16) ? 0 : 2; | |
| 34711 | + if( k-i>16 ) return 2; | |
| 34712 | + if( z[k]!=0 ) return 1; | |
| 34713 | + return 0; | |
| 34611 | 34714 | }else |
| 34612 | 34715 | #endif /* SQLITE_OMIT_HEX_INTEGER */ |
| 34613 | 34716 | { |
| 34614 | 34717 | return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); |
| 34615 | 34718 | } |
| @@ -34641,11 +34744,11 @@ | ||
| 34641 | 34744 | && sqlite3Isxdigit(zNum[2]) |
| 34642 | 34745 | ){ |
| 34643 | 34746 | u32 u = 0; |
| 34644 | 34747 | zNum += 2; |
| 34645 | 34748 | while( zNum[0]=='0' ) zNum++; |
| 34646 | - for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ | |
| 34749 | + for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ | |
| 34647 | 34750 | u = u*16 + sqlite3HexToInt(zNum[i]); |
| 34648 | 34751 | } |
| 34649 | 34752 | if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ |
| 34650 | 34753 | memcpy(pValue, &u, 4); |
| 34651 | 34754 | return 1; |
| @@ -37137,11 +37240,11 @@ | ||
| 37137 | 37240 | # define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 37138 | 37241 | # endif |
| 37139 | 37242 | #endif |
| 37140 | 37243 | |
| 37141 | 37244 | /* Use pread() and pwrite() if they are available */ |
| 37142 | -#if defined(__APPLE__) | |
| 37245 | +#if defined(__APPLE__) || defined(__linux__) | |
| 37143 | 37246 | # define HAVE_PREAD 1 |
| 37144 | 37247 | # define HAVE_PWRITE 1 |
| 37145 | 37248 | #endif |
| 37146 | 37249 | #if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) |
| 37147 | 37250 | # undef USE_PREAD |
| @@ -40387,16 +40490,10 @@ | ||
| 40387 | 40490 | |
| 40388 | 40491 | /* |
| 40389 | 40492 | ** Seek to the offset passed as the second argument, then read cnt |
| 40390 | 40493 | ** bytes into pBuf. Return the number of bytes actually read. |
| 40391 | 40494 | ** |
| 40392 | -** NB: If you define USE_PREAD or USE_PREAD64, then it might also | |
| 40393 | -** be necessary to define _XOPEN_SOURCE to be 500. This varies from | |
| 40394 | -** one system to another. Since SQLite does not define USE_PREAD | |
| 40395 | -** in any form by default, we will not attempt to define _XOPEN_SOURCE. | |
| 40396 | -** See tickets #2741 and #2681. | |
| 40397 | -** | |
| 40398 | 40495 | ** To avoid stomping the errno value on a failed read the lastErrno value |
| 40399 | 40496 | ** is set before returning. |
| 40400 | 40497 | */ |
| 40401 | 40498 | static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ |
| 40402 | 40499 | int got; |
| @@ -50419,11 +50516,11 @@ | ||
| 50419 | 50516 | &extendedParameters); |
| 50420 | 50517 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50421 | 50518 | if( isReadWrite ){ |
| 50422 | 50519 | int rc2, isRO = 0; |
| 50423 | 50520 | sqlite3BeginBenignMalloc(); |
| 50424 | - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); | |
| 50521 | + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); | |
| 50425 | 50522 | sqlite3EndBenignMalloc(); |
| 50426 | 50523 | if( rc2==SQLITE_OK && isRO ) break; |
| 50427 | 50524 | } |
| 50428 | 50525 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50429 | 50526 | #else |
| @@ -50436,11 +50533,11 @@ | ||
| 50436 | 50533 | NULL); |
| 50437 | 50534 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50438 | 50535 | if( isReadWrite ){ |
| 50439 | 50536 | int rc2, isRO = 0; |
| 50440 | 50537 | sqlite3BeginBenignMalloc(); |
| 50441 | - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); | |
| 50538 | + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); | |
| 50442 | 50539 | sqlite3EndBenignMalloc(); |
| 50443 | 50540 | if( rc2==SQLITE_OK && isRO ) break; |
| 50444 | 50541 | } |
| 50445 | 50542 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50446 | 50543 | #endif |
| @@ -50456,11 +50553,11 @@ | ||
| 50456 | 50553 | NULL); |
| 50457 | 50554 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50458 | 50555 | if( isReadWrite ){ |
| 50459 | 50556 | int rc2, isRO = 0; |
| 50460 | 50557 | sqlite3BeginBenignMalloc(); |
| 50461 | - rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); | |
| 50558 | + rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); | |
| 50462 | 50559 | sqlite3EndBenignMalloc(); |
| 50463 | 50560 | if( rc2==SQLITE_OK && isRO ) break; |
| 50464 | 50561 | } |
| 50465 | 50562 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50466 | 50563 | } |
| @@ -50678,10 +50775,17 @@ | ||
| 50678 | 50775 | UNUSED_PARAMETER(pVfs); |
| 50679 | 50776 | |
| 50680 | 50777 | SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
| 50681 | 50778 | OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", |
| 50682 | 50779 | zFilename, flags, pResOut)); |
| 50780 | + | |
| 50781 | + if( zFilename==0 ){ | |
| 50782 | + *pResOut = 0; | |
| 50783 | + OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", | |
| 50784 | + zFilename, pResOut, *pResOut)); | |
| 50785 | + return SQLITE_OK; | |
| 50786 | + } | |
| 50683 | 50787 | |
| 50684 | 50788 | zConverted = winConvertFromUtf8Filename(zFilename); |
| 50685 | 50789 | if( zConverted==0 ){ |
| 50686 | 50790 | OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); |
| 50687 | 50791 | return SQLITE_IOERR_NOMEM_BKPT; |
| @@ -52835,15 +52939,19 @@ | ||
| 52835 | 52939 | # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} |
| 52836 | 52940 | static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){ |
| 52837 | 52941 | PgHdr *pPg; |
| 52838 | 52942 | unsigned char *a; |
| 52839 | 52943 | int j; |
| 52840 | - pPg = (PgHdr*)pLower->pExtra; | |
| 52841 | - printf("%3lld: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); | |
| 52842 | - a = (unsigned char *)pLower->pBuf; | |
| 52843 | - for(j=0; j<12; j++) printf("%02x", a[j]); | |
| 52844 | - printf(" ptr %p\n", pPg); | |
| 52944 | + if( pLower==0 ){ | |
| 52945 | + printf("%3d: NULL\n", i); | |
| 52946 | + }else{ | |
| 52947 | + pPg = (PgHdr*)pLower->pExtra; | |
| 52948 | + printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags); | |
| 52949 | + a = (unsigned char *)pLower->pBuf; | |
| 52950 | + for(j=0; j<12; j++) printf("%02x", a[j]); | |
| 52951 | + printf(" ptr %p\n", pPg); | |
| 52952 | + } | |
| 52845 | 52953 | } |
| 52846 | 52954 | static void pcacheDump(PCache *pCache){ |
| 52847 | 52955 | int N; |
| 52848 | 52956 | int i; |
| 52849 | 52957 | sqlite3_pcache_page *pLower; |
| @@ -52852,13 +52960,12 @@ | ||
| 52852 | 52960 | if( pCache->pCache==0 ) return; |
| 52853 | 52961 | N = sqlite3PcachePagecount(pCache); |
| 52854 | 52962 | if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; |
| 52855 | 52963 | for(i=1; i<=N; i++){ |
| 52856 | 52964 | pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); |
| 52857 | - if( pLower==0 ) continue; | |
| 52858 | 52965 | pcachePageTrace(i, pLower); |
| 52859 | - if( ((PgHdr*)pLower)->pPage==0 ){ | |
| 52966 | + if( pLower && ((PgHdr*)pLower)->pPage==0 ){ | |
| 52860 | 52967 | sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); |
| 52861 | 52968 | } |
| 52862 | 52969 | } |
| 52863 | 52970 | } |
| 52864 | 52971 | #else |
| @@ -58242,10 +58349,12 @@ | ||
| 58242 | 58349 | */ |
| 58243 | 58350 | static int pager_truncate(Pager *pPager, Pgno nPage){ |
| 58244 | 58351 | int rc = SQLITE_OK; |
| 58245 | 58352 | assert( pPager->eState!=PAGER_ERROR ); |
| 58246 | 58353 | assert( pPager->eState!=PAGER_READER ); |
| 58354 | + PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage)); | |
| 58355 | + | |
| 58247 | 58356 | |
| 58248 | 58357 | if( isOpen(pPager->fd) |
| 58249 | 58358 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| 58250 | 58359 | ){ |
| 58251 | 58360 | i64 currentSize, newSize; |
| @@ -61159,10 +61268,14 @@ | ||
| 61159 | 61268 | |
| 61160 | 61269 | assert( !isOpen(pPager->fd) || !MEMDB ); |
| 61161 | 61270 | if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ |
| 61162 | 61271 | if( pgno>pPager->mxPgno ){ |
| 61163 | 61272 | rc = SQLITE_FULL; |
| 61273 | + if( pgno<=pPager->dbSize ){ | |
| 61274 | + sqlite3PcacheRelease(pPg); | |
| 61275 | + pPg = 0; | |
| 61276 | + } | |
| 61164 | 61277 | goto pager_acquire_err; |
| 61165 | 61278 | } |
| 61166 | 61279 | if( noContent ){ |
| 61167 | 61280 | /* Failure to set the bits in the InJournal bit-vectors is benign. |
| 61168 | 61281 | ** It merely means that we might do some extra work to journal a |
| @@ -61323,14 +61436,16 @@ | ||
| 61323 | 61436 | } |
| 61324 | 61437 | |
| 61325 | 61438 | /* |
| 61326 | 61439 | ** Release a page reference. |
| 61327 | 61440 | ** |
| 61328 | -** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be | |
| 61329 | -** used if we know that the page being released is not the last page. | |
| 61441 | +** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used | |
| 61442 | +** if we know that the page being released is not the last reference to page1. | |
| 61330 | 61443 | ** The btree layer always holds page1 open until the end, so these first |
| 61331 | -** to routines can be used to release any page other than BtShared.pPage1. | |
| 61444 | +** two routines can be used to release any page other than BtShared.pPage1. | |
| 61445 | +** The assert() at tag-20230419-2 proves that this constraint is always | |
| 61446 | +** honored. | |
| 61332 | 61447 | ** |
| 61333 | 61448 | ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine |
| 61334 | 61449 | ** checks the total number of outstanding pages and if the number of |
| 61335 | 61450 | ** pages reaches zero it drops the database lock. |
| 61336 | 61451 | */ |
| @@ -61342,11 +61457,11 @@ | ||
| 61342 | 61457 | pagerReleaseMapPage(pPg); |
| 61343 | 61458 | }else{ |
| 61344 | 61459 | sqlite3PcacheRelease(pPg); |
| 61345 | 61460 | } |
| 61346 | 61461 | /* Do not use this routine to release the last reference to page1 */ |
| 61347 | - assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); | |
| 61462 | + assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */ | |
| 61348 | 61463 | } |
| 61349 | 61464 | SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ |
| 61350 | 61465 | if( pPg ) sqlite3PagerUnrefNotNull(pPg); |
| 61351 | 61466 | } |
| 61352 | 61467 | SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){ |
| @@ -63102,17 +63217,19 @@ | ||
| 63102 | 63217 | ** Attempt to take an exclusive lock on the database file. If a PENDING lock |
| 63103 | 63218 | ** is obtained instead, immediately release it. |
| 63104 | 63219 | */ |
| 63105 | 63220 | static int pagerExclusiveLock(Pager *pPager){ |
| 63106 | 63221 | int rc; /* Return code */ |
| 63222 | + u8 eOrigLock; /* Original lock */ | |
| 63107 | 63223 | |
| 63108 | - assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); | |
| 63224 | + assert( pPager->eLock>=SHARED_LOCK ); | |
| 63225 | + eOrigLock = pPager->eLock; | |
| 63109 | 63226 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 63110 | 63227 | if( rc!=SQLITE_OK ){ |
| 63111 | 63228 | /* If the attempt to grab the exclusive lock failed, release the |
| 63112 | 63229 | ** pending lock that may have been obtained instead. */ |
| 63113 | - pagerUnlockDb(pPager, SHARED_LOCK); | |
| 63230 | + pagerUnlockDb(pPager, eOrigLock); | |
| 63114 | 63231 | } |
| 63115 | 63232 | |
| 63116 | 63233 | return rc; |
| 63117 | 63234 | } |
| 63118 | 63235 | |
| @@ -64113,23 +64230,44 @@ | ||
| 64113 | 64230 | } |
| 64114 | 64231 | |
| 64115 | 64232 | assert( nByte>=8 ); |
| 64116 | 64233 | assert( (nByte&0x00000007)==0 ); |
| 64117 | 64234 | assert( nByte<=65536 ); |
| 64235 | + assert( nByte%4==0 ); | |
| 64118 | 64236 | |
| 64119 | - if( nativeCksum ){ | |
| 64120 | - do { | |
| 64121 | - s1 += *aData++ + s2; | |
| 64122 | - s2 += *aData++ + s1; | |
| 64123 | - }while( aData<aEnd ); | |
| 64124 | - }else{ | |
| 64237 | + if( !nativeCksum ){ | |
| 64125 | 64238 | do { |
| 64126 | 64239 | s1 += BYTESWAP32(aData[0]) + s2; |
| 64127 | 64240 | s2 += BYTESWAP32(aData[1]) + s1; |
| 64128 | 64241 | aData += 2; |
| 64129 | 64242 | }while( aData<aEnd ); |
| 64243 | + }else if( nByte%64==0 ){ | |
| 64244 | + do { | |
| 64245 | + s1 += *aData++ + s2; | |
| 64246 | + s2 += *aData++ + s1; | |
| 64247 | + s1 += *aData++ + s2; | |
| 64248 | + s2 += *aData++ + s1; | |
| 64249 | + s1 += *aData++ + s2; | |
| 64250 | + s2 += *aData++ + s1; | |
| 64251 | + s1 += *aData++ + s2; | |
| 64252 | + s2 += *aData++ + s1; | |
| 64253 | + s1 += *aData++ + s2; | |
| 64254 | + s2 += *aData++ + s1; | |
| 64255 | + s1 += *aData++ + s2; | |
| 64256 | + s2 += *aData++ + s1; | |
| 64257 | + s1 += *aData++ + s2; | |
| 64258 | + s2 += *aData++ + s1; | |
| 64259 | + s1 += *aData++ + s2; | |
| 64260 | + s2 += *aData++ + s1; | |
| 64261 | + }while( aData<aEnd ); | |
| 64262 | + }else{ | |
| 64263 | + do { | |
| 64264 | + s1 += *aData++ + s2; | |
| 64265 | + s2 += *aData++ + s1; | |
| 64266 | + }while( aData<aEnd ); | |
| 64130 | 64267 | } |
| 64268 | + assert( aData==aEnd ); | |
| 64131 | 64269 | |
| 64132 | 64270 | aOut[0] = s1; |
| 64133 | 64271 | aOut[1] = s2; |
| 64134 | 64272 | } |
| 64135 | 64273 | |
| @@ -67056,11 +67194,13 @@ | ||
| 67056 | 67194 | if( pWal->syncHeader ){ |
| 67057 | 67195 | rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 67058 | 67196 | if( rc ) return rc; |
| 67059 | 67197 | } |
| 67060 | 67198 | } |
| 67061 | - assert( (int)pWal->szPage==szPage ); | |
| 67199 | + if( (int)pWal->szPage!=szPage ){ | |
| 67200 | + return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */ | |
| 67201 | + } | |
| 67062 | 67202 | |
| 67063 | 67203 | /* Setup information needed to write frames into the WAL */ |
| 67064 | 67204 | w.pWal = pWal; |
| 67065 | 67205 | w.pFd = pWal->pWalFd; |
| 67066 | 67206 | w.iSyncPoint = 0; |
| @@ -67716,11 +67856,11 @@ | ||
| 67716 | 67856 | ** Cell content makes use of variable length integers. A variable |
| 67717 | 67857 | ** length integer is 1 to 9 bytes where the lower 7 bits of each |
| 67718 | 67858 | ** byte are used. The integer consists of all bytes that have bit 8 set and |
| 67719 | 67859 | ** the first byte with bit 8 clear. The most significant byte of the integer |
| 67720 | 67860 | ** appears first. A variable-length integer may not be more than 9 bytes long. |
| 67721 | -** As a special case, all 8 bytes of the 9th byte are used as data. This | |
| 67861 | +** As a special case, all 8 bits of the 9th byte are used as data. This | |
| 67722 | 67862 | ** allows a 64-bit integer to be encoded in 9 bytes. |
| 67723 | 67863 | ** |
| 67724 | 67864 | ** 0x00 becomes 0x00000000 |
| 67725 | 67865 | ** 0x7f becomes 0x0000007f |
| 67726 | 67866 | ** 0x81 0x00 becomes 0x00000080 |
| @@ -68100,11 +68240,11 @@ | ||
| 68100 | 68240 | ** Legal values for BtCursor.curFlags |
| 68101 | 68241 | */ |
| 68102 | 68242 | #define BTCF_WriteFlag 0x01 /* True if a write cursor */ |
| 68103 | 68243 | #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ |
| 68104 | 68244 | #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ |
| 68105 | -#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ | |
| 68245 | +#define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ | |
| 68106 | 68246 | #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ |
| 68107 | 68247 | #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ |
| 68108 | 68248 | #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ |
| 68109 | 68249 | |
| 68110 | 68250 | /* |
| @@ -76202,11 +76342,11 @@ | ||
| 76202 | 76342 | aAfter[j] = iAfter; |
| 76203 | 76343 | break; |
| 76204 | 76344 | } |
| 76205 | 76345 | } |
| 76206 | 76346 | if( j>=nFree ){ |
| 76207 | - if( nFree>=sizeof(aOfst)/sizeof(aOfst[0]) ){ | |
| 76347 | + if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){ | |
| 76208 | 76348 | for(j=0; j<nFree; j++){ |
| 76209 | 76349 | freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]); |
| 76210 | 76350 | } |
| 76211 | 76351 | nFree = 0; |
| 76212 | 76352 | } |
| @@ -76270,11 +76410,11 @@ | ||
| 76270 | 76410 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| 76271 | 76411 | assert( nCell>=nTail ); |
| 76272 | 76412 | nCell -= nTail; |
| 76273 | 76413 | } |
| 76274 | 76414 | |
| 76275 | - pData = &aData[get2byteNotZero(&aData[hdr+5])]; | |
| 76415 | + pData = &aData[get2byte(&aData[hdr+5])]; | |
| 76276 | 76416 | if( pData<pBegin ) goto editpage_fail; |
| 76277 | 76417 | if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail; |
| 76278 | 76418 | |
| 76279 | 76419 | /* Add cells to the start of the page */ |
| 76280 | 76420 | if( iNew<iOld ){ |
| @@ -77907,11 +78047,11 @@ | ||
| 77907 | 78047 | return btreeOverwriteCell(pCur, &x2); |
| 77908 | 78048 | } |
| 77909 | 78049 | } |
| 77910 | 78050 | } |
| 77911 | 78051 | assert( pCur->eState==CURSOR_VALID |
| 77912 | - || (pCur->eState==CURSOR_INVALID && loc) ); | |
| 78052 | + || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); | |
| 77913 | 78053 | |
| 77914 | 78054 | pPage = pCur->pPage; |
| 77915 | 78055 | assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); |
| 77916 | 78056 | assert( pPage->leaf || !pPage->intKey ); |
| 77917 | 78057 | if( pPage->nFree<0 ){ |
| @@ -80157,17 +80297,11 @@ | ||
| 80157 | 80297 | assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); |
| 80158 | 80298 | assert( p->bDestLocked ); |
| 80159 | 80299 | assert( !isFatalError(p->rc) ); |
| 80160 | 80300 | assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); |
| 80161 | 80301 | assert( zSrcData ); |
| 80162 | - | |
| 80163 | - /* Catch the case where the destination is an in-memory database and the | |
| 80164 | - ** page sizes of the source and destination differ. | |
| 80165 | - */ | |
| 80166 | - if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ | |
| 80167 | - rc = SQLITE_READONLY; | |
| 80168 | - } | |
| 80302 | + assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); | |
| 80169 | 80303 | |
| 80170 | 80304 | /* This loop runs once for each destination page spanned by the source |
| 80171 | 80305 | ** page. For each iteration, variable iOff is set to the byte offset |
| 80172 | 80306 | ** of the destination page. |
| 80173 | 80307 | */ |
| @@ -80296,11 +80430,14 @@ | ||
| 80296 | 80430 | /* Do not allow backup if the destination database is in WAL mode |
| 80297 | 80431 | ** and the page sizes are different between source and destination */ |
| 80298 | 80432 | pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); |
| 80299 | 80433 | pgszDest = sqlite3BtreeGetPageSize(p->pDest); |
| 80300 | 80434 | destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); |
| 80301 | - if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ | |
| 80435 | + if( SQLITE_OK==rc | |
| 80436 | + && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) | |
| 80437 | + && pgszSrc!=pgszDest | |
| 80438 | + ){ | |
| 80302 | 80439 | rc = SQLITE_READONLY; |
| 80303 | 80440 | } |
| 80304 | 80441 | |
| 80305 | 80442 | /* Now that there is a read-lock on the source database, query the |
| 80306 | 80443 | ** source pager for the number of pages in the database. |
| @@ -80845,10 +80982,11 @@ | ||
| 80845 | 80982 | Mem tmp; |
| 80846 | 80983 | char zBuf[100]; |
| 80847 | 80984 | char *z; |
| 80848 | 80985 | int i, j, incr; |
| 80849 | 80986 | if( (p->flags & MEM_Str)==0 ) return 1; |
| 80987 | + if( p->db && p->db->mallocFailed ) return 1; | |
| 80850 | 80988 | if( p->flags & MEM_Term ){ |
| 80851 | 80989 | /* Insure that the string is properly zero-terminated. Pay particular |
| 80852 | 80990 | ** attention to the case where p->n is odd */ |
| 80853 | 80991 | if( p->szMalloc>0 && p->z==p->zMalloc ){ |
| 80854 | 80992 | assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 ); |
| @@ -83508,10 +83646,12 @@ | ||
| 83508 | 83646 | static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ |
| 83509 | 83647 | int nMaxArgs = *pMaxFuncArgs; |
| 83510 | 83648 | Op *pOp; |
| 83511 | 83649 | Parse *pParse = p->pParse; |
| 83512 | 83650 | int *aLabel = pParse->aLabel; |
| 83651 | + | |
| 83652 | + assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */ | |
| 83513 | 83653 | p->readOnly = 1; |
| 83514 | 83654 | p->bIsReader = 0; |
| 83515 | 83655 | pOp = &p->aOp[p->nOp-1]; |
| 83516 | 83656 | assert( p->aOp[0].opcode==OP_Init ); |
| 83517 | 83657 | while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){ |
| @@ -83567,10 +83707,11 @@ | ||
| 83567 | 83707 | /* The mkopcodeh.tcl script has so arranged things that the only |
| 83568 | 83708 | ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to |
| 83569 | 83709 | ** have non-negative values for P2. */ |
| 83570 | 83710 | assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); |
| 83571 | 83711 | assert( ADDR(pOp->p2)<-pParse->nLabel ); |
| 83712 | + assert( aLabel!=0 ); /* True because of tag-20230419-1 */ | |
| 83572 | 83713 | pOp->p2 = aLabel[ADDR(pOp->p2)]; |
| 83573 | 83714 | } |
| 83574 | 83715 | break; |
| 83575 | 83716 | } |
| 83576 | 83717 | } |
| @@ -84310,11 +84451,11 @@ | ||
| 84310 | 84451 | } |
| 84311 | 84452 | } |
| 84312 | 84453 | |
| 84313 | 84454 | /* Return the most recently added opcode |
| 84314 | 84455 | */ |
| 84315 | -VdbeOp * sqlite3VdbeGetLastOp(Vdbe *p){ | |
| 84456 | +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ | |
| 84316 | 84457 | return sqlite3VdbeGetOp(p, p->nOp - 1); |
| 84317 | 84458 | } |
| 84318 | 84459 | |
| 84319 | 84460 | #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) |
| 84320 | 84461 | /* |
| @@ -88039,10 +88180,20 @@ | ||
| 88039 | 88180 | sqlite3 *db = v->db; |
| 88040 | 88181 | i64 iKey2; |
| 88041 | 88182 | PreUpdate preupdate; |
| 88042 | 88183 | const char *zTbl = pTab->zName; |
| 88043 | 88184 | static const u8 fakeSortOrder = 0; |
| 88185 | +#ifdef SQLITE_DEBUG | |
| 88186 | + int nRealCol; | |
| 88187 | + if( pTab->tabFlags & TF_WithoutRowid ){ | |
| 88188 | + nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn; | |
| 88189 | + }else if( pTab->tabFlags & TF_HasVirtual ){ | |
| 88190 | + nRealCol = pTab->nNVCol; | |
| 88191 | + }else{ | |
| 88192 | + nRealCol = pTab->nCol; | |
| 88193 | + } | |
| 88194 | +#endif | |
| 88044 | 88195 | |
| 88045 | 88196 | assert( db->pPreUpdate==0 ); |
| 88046 | 88197 | memset(&preupdate, 0, sizeof(PreUpdate)); |
| 88047 | 88198 | if( HasRowid(pTab)==0 ){ |
| 88048 | 88199 | iKey1 = iKey2 = 0; |
| @@ -88055,12 +88206,12 @@ | ||
| 88055 | 88206 | } |
| 88056 | 88207 | } |
| 88057 | 88208 | |
| 88058 | 88209 | assert( pCsr!=0 ); |
| 88059 | 88210 | assert( pCsr->eCurType==CURTYPE_BTREE ); |
| 88060 | - assert( pCsr->nField==pTab->nCol | |
| 88061 | - || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) | |
| 88211 | + assert( pCsr->nField==nRealCol | |
| 88212 | + || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1) | |
| 88062 | 88213 | ); |
| 88063 | 88214 | |
| 88064 | 88215 | preupdate.v = v; |
| 88065 | 88216 | preupdate.pCsr = pCsr; |
| 88066 | 88217 | preupdate.op = op; |
| @@ -89429,13 +89580,13 @@ | ||
| 89429 | 89580 | p = (Vdbe *)pStmt; |
| 89430 | 89581 | db = p->db; |
| 89431 | 89582 | assert( db!=0 ); |
| 89432 | 89583 | n = sqlite3_column_count(pStmt); |
| 89433 | 89584 | if( N<n && N>=0 ){ |
| 89585 | + u8 prior_mallocFailed = db->mallocFailed; | |
| 89434 | 89586 | N += useType*n; |
| 89435 | 89587 | sqlite3_mutex_enter(db->mutex); |
| 89436 | - assert( db->mallocFailed==0 ); | |
| 89437 | 89588 | #ifndef SQLITE_OMIT_UTF16 |
| 89438 | 89589 | if( useUtf16 ){ |
| 89439 | 89590 | ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); |
| 89440 | 89591 | }else |
| 89441 | 89592 | #endif |
| @@ -89443,11 +89594,12 @@ | ||
| 89443 | 89594 | ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); |
| 89444 | 89595 | } |
| 89445 | 89596 | /* A malloc may have failed inside of the _text() call. If this |
| 89446 | 89597 | ** is the case, clear the mallocFailed flag and return NULL. |
| 89447 | 89598 | */ |
| 89448 | - if( db->mallocFailed ){ | |
| 89599 | + assert( db->mallocFailed==0 || db->mallocFailed==1 ); | |
| 89600 | + if( db->mallocFailed > prior_mallocFailed ){ | |
| 89449 | 89601 | sqlite3OomClear(db); |
| 89450 | 89602 | ret = 0; |
| 89451 | 89603 | } |
| 89452 | 89604 | sqlite3_mutex_leave(db->mutex); |
| 89453 | 89605 | } |
| @@ -92987,11 +93139,11 @@ | ||
| 92987 | 93139 | } |
| 92988 | 93140 | |
| 92989 | 93141 | /* Opcode: Jump P1 P2 P3 * * |
| 92990 | 93142 | ** |
| 92991 | 93143 | ** Jump to the instruction at address P1, P2, or P3 depending on whether |
| 92992 | -** in the most recent OP_Compare instruction the P1 vector was less than | |
| 93144 | +** in the most recent OP_Compare instruction the P1 vector was less than, | |
| 92993 | 93145 | ** equal to, or greater than the P2 vector, respectively. |
| 92994 | 93146 | ** |
| 92995 | 93147 | ** This opcode must immediately follow an OP_Compare opcode. |
| 92996 | 93148 | */ |
| 92997 | 93149 | case OP_Jump: { /* jump */ |
| @@ -93333,11 +93485,11 @@ | ||
| 93333 | 93485 | */ |
| 93334 | 93486 | case OP_IfNullRow: { /* jump */ |
| 93335 | 93487 | VdbeCursor *pC; |
| 93336 | 93488 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 93337 | 93489 | pC = p->apCsr[pOp->p1]; |
| 93338 | - if( ALWAYS(pC) && pC->nullRow ){ | |
| 93490 | + if( pC && pC->nullRow ){ | |
| 93339 | 93491 | sqlite3VdbeMemSetNull(aMem + pOp->p3); |
| 93340 | 93492 | goto jump_to_p2; |
| 93341 | 93493 | } |
| 93342 | 93494 | break; |
| 93343 | 93495 | } |
| @@ -93828,11 +93980,11 @@ | ||
| 93828 | 93980 | pIn1->flags |= MEM_IntReal; |
| 93829 | 93981 | pIn1->flags &= ~MEM_Int; |
| 93830 | 93982 | }else{ |
| 93831 | 93983 | pIn1->u.r = (double)pIn1->u.i; |
| 93832 | 93984 | pIn1->flags |= MEM_Real; |
| 93833 | - pIn1->flags &= ~MEM_Int; | |
| 93985 | + pIn1->flags &= ~(MEM_Int|MEM_Str); | |
| 93834 | 93986 | } |
| 93835 | 93987 | } |
| 93836 | 93988 | REGISTER_TRACE((int)(pIn1-aMem), pIn1); |
| 93837 | 93989 | zAffinity++; |
| 93838 | 93990 | if( zAffinity[0]==0 ) break; |
| @@ -99841,11 +99993,11 @@ | ||
| 99841 | 99993 | *ppBlob = (sqlite3_blob *)pBlob; |
| 99842 | 99994 | }else{ |
| 99843 | 99995 | if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); |
| 99844 | 99996 | sqlite3DbFree(db, pBlob); |
| 99845 | 99997 | } |
| 99846 | - sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); | |
| 99998 | + sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); | |
| 99847 | 99999 | sqlite3DbFree(db, zErr); |
| 99848 | 100000 | sqlite3ParseObjectReset(&sParse); |
| 99849 | 100001 | rc = sqlite3ApiExit(db, rc); |
| 99850 | 100002 | sqlite3_mutex_leave(db->mutex); |
| 99851 | 100003 | return rc; |
| @@ -100000,11 +100152,11 @@ | ||
| 100000 | 100152 | }else{ |
| 100001 | 100153 | char *zErr; |
| 100002 | 100154 | ((Vdbe*)p->pStmt)->rc = SQLITE_OK; |
| 100003 | 100155 | rc = blobSeekToRow(p, iRow, &zErr); |
| 100004 | 100156 | if( rc!=SQLITE_OK ){ |
| 100005 | - sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); | |
| 100157 | + sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); | |
| 100006 | 100158 | sqlite3DbFree(db, zErr); |
| 100007 | 100159 | } |
| 100008 | 100160 | assert( rc!=SQLITE_SCHEMA ); |
| 100009 | 100161 | } |
| 100010 | 100162 | |
| @@ -107198,28 +107350,31 @@ | ||
| 107198 | 107350 | |
| 107199 | 107351 | /* |
| 107200 | 107352 | ** Join two expressions using an AND operator. If either expression is |
| 107201 | 107353 | ** NULL, then just return the other expression. |
| 107202 | 107354 | ** |
| 107203 | -** If one side or the other of the AND is known to be false, then instead | |
| 107204 | -** of returning an AND expression, just return a constant expression with | |
| 107205 | -** a value of false. | |
| 107355 | +** If one side or the other of the AND is known to be false, and neither side | |
| 107356 | +** is part of an ON clause, then instead of returning an AND expression, | |
| 107357 | +** just return a constant expression with a value of false. | |
| 107206 | 107358 | */ |
| 107207 | 107359 | SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ |
| 107208 | 107360 | sqlite3 *db = pParse->db; |
| 107209 | 107361 | if( pLeft==0 ){ |
| 107210 | 107362 | return pRight; |
| 107211 | 107363 | }else if( pRight==0 ){ |
| 107212 | 107364 | return pLeft; |
| 107213 | - }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) | |
| 107214 | - && !IN_RENAME_OBJECT | |
| 107215 | - ){ | |
| 107216 | - sqlite3ExprDeferredDelete(pParse, pLeft); | |
| 107217 | - sqlite3ExprDeferredDelete(pParse, pRight); | |
| 107218 | - return sqlite3Expr(db, TK_INTEGER, "0"); | |
| 107219 | 107365 | }else{ |
| 107220 | - return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); | |
| 107366 | + u32 f = pLeft->flags | pRight->flags; | |
| 107367 | + if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse))==EP_IsFalse | |
| 107368 | + && !IN_RENAME_OBJECT | |
| 107369 | + ){ | |
| 107370 | + sqlite3ExprDeferredDelete(pParse, pLeft); | |
| 107371 | + sqlite3ExprDeferredDelete(pParse, pRight); | |
| 107372 | + return sqlite3Expr(db, TK_INTEGER, "0"); | |
| 107373 | + }else{ | |
| 107374 | + return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); | |
| 107375 | + } | |
| 107221 | 107376 | } |
| 107222 | 107377 | } |
| 107223 | 107378 | |
| 107224 | 107379 | /* |
| 107225 | 107380 | ** Construct a new expression node for a function with multiple |
| @@ -108460,16 +108615,21 @@ | ||
| 108460 | 108615 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ |
| 108461 | 108616 | return exprIsConst(p, 3, iCur); |
| 108462 | 108617 | } |
| 108463 | 108618 | |
| 108464 | 108619 | /* |
| 108465 | -** Check pExpr to see if it is an invariant constraint on data source pSrc. | |
| 108620 | +** Check pExpr to see if it is an constraint on the single data source | |
| 108621 | +** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr | |
| 108622 | +** constrains pSrc but does not depend on any other tables or data | |
| 108623 | +** sources anywhere else in the query. Return true (non-zero) if pExpr | |
| 108624 | +** is a constraint on pSrc only. | |
| 108625 | +** | |
| 108466 | 108626 | ** This is an optimization. False negatives will perhaps cause slower |
| 108467 | 108627 | ** queries, but false positives will yield incorrect answers. So when in |
| 108468 | 108628 | ** doubt, return 0. |
| 108469 | 108629 | ** |
| 108470 | -** To be an invariant constraint, the following must be true: | |
| 108630 | +** To be an single-source constraint, the following must be true: | |
| 108471 | 108631 | ** |
| 108472 | 108632 | ** (1) pExpr cannot refer to any table other than pSrc->iCursor. |
| 108473 | 108633 | ** |
| 108474 | 108634 | ** (2) pExpr cannot use subqueries or non-deterministic functions. |
| 108475 | 108635 | ** |
| @@ -108476,25 +108636,56 @@ | ||
| 108476 | 108636 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. |
| 108477 | 108637 | ** (Is there some way to relax this constraint?) |
| 108478 | 108638 | ** |
| 108479 | 108639 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 108480 | 108640 | ** (4a) pExpr must come from an ON clause.. |
| 108481 | - (4b) and specifically the ON clause associated with the LEFT JOIN. | |
| 108641 | +** (4b) and specifically the ON clause associated with the LEFT JOIN. | |
| 108482 | 108642 | ** |
| 108483 | 108643 | ** (5) If pSrc is not the right operand of a LEFT JOIN or the left |
| 108484 | 108644 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 108485 | 108645 | ** clause, not an ON clause. |
| 108646 | +** | |
| 108647 | +** (6) Either: | |
| 108648 | +** | |
| 108649 | +** (6a) pExpr does not originate in an ON or USING clause, or | |
| 108650 | +** | |
| 108651 | +** (6b) The ON or USING clause from which pExpr is derived is | |
| 108652 | +** not to the left of a RIGHT JOIN (or FULL JOIN). | |
| 108653 | +** | |
| 108654 | +** Without this restriction, accepting pExpr as a single-table | |
| 108655 | +** constraint might move the the ON/USING filter expression | |
| 108656 | +** from the left side of a RIGHT JOIN over to the right side, | |
| 108657 | +** which leads to incorrect answers. See also restriction (9) | |
| 108658 | +** on push-down. | |
| 108486 | 108659 | */ |
| 108487 | -SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ | |
| 108660 | +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( | |
| 108661 | + Expr *pExpr, /* The constraint */ | |
| 108662 | + const SrcList *pSrcList, /* Complete FROM clause */ | |
| 108663 | + int iSrc /* Which element of pSrcList to use */ | |
| 108664 | +){ | |
| 108665 | + const SrcItem *pSrc = &pSrcList->a[iSrc]; | |
| 108488 | 108666 | if( pSrc->fg.jointype & JT_LTORJ ){ |
| 108489 | 108667 | return 0; /* rule (3) */ |
| 108490 | 108668 | } |
| 108491 | 108669 | if( pSrc->fg.jointype & JT_LEFT ){ |
| 108492 | 108670 | if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ |
| 108493 | 108671 | if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ |
| 108494 | 108672 | }else{ |
| 108495 | 108673 | if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ |
| 108674 | + } | |
| 108675 | + if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */ | |
| 108676 | + && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */ | |
| 108677 | + ){ | |
| 108678 | + int jj; | |
| 108679 | + for(jj=0; jj<iSrc; jj++){ | |
| 108680 | + if( pExpr->w.iJoin==pSrcList->a[jj].iCursor ){ | |
| 108681 | + if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ | |
| 108682 | + return 0; /* restriction (6) */ | |
| 108683 | + } | |
| 108684 | + break; | |
| 108685 | + } | |
| 108686 | + } | |
| 108496 | 108687 | } |
| 108497 | 108688 | return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ |
| 108498 | 108689 | } |
| 108499 | 108690 | |
| 108500 | 108691 | |
| @@ -112373,11 +112564,11 @@ | ||
| 112373 | 112564 | int iAgg = pExpr->iAgg; |
| 112374 | 112565 | Parse *pParse = pWalker->pParse; |
| 112375 | 112566 | sqlite3 *db = pParse->db; |
| 112376 | 112567 | assert( iAgg>=0 ); |
| 112377 | 112568 | if( pExpr->op!=TK_AGG_FUNCTION ){ |
| 112378 | - if( ALWAYS(iAgg<pAggInfo->nColumn) | |
| 112569 | + if( iAgg<pAggInfo->nColumn | |
| 112379 | 112570 | && pAggInfo->aCol[iAgg].pCExpr==pExpr |
| 112380 | 112571 | ){ |
| 112381 | 112572 | pExpr = sqlite3ExprDup(db, pExpr, 0); |
| 112382 | 112573 | if( pExpr ){ |
| 112383 | 112574 | pAggInfo->aCol[iAgg].pCExpr = pExpr; |
| @@ -112578,11 +112769,11 @@ | ||
| 112578 | 112769 | findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); |
| 112579 | 112770 | break; |
| 112580 | 112771 | } /* endif pExpr->iTable==pItem->iCursor */ |
| 112581 | 112772 | } /* end loop over pSrcList */ |
| 112582 | 112773 | } |
| 112583 | - return WRC_Prune; | |
| 112774 | + return WRC_Continue; | |
| 112584 | 112775 | } |
| 112585 | 112776 | case TK_AGG_FUNCTION: { |
| 112586 | 112777 | if( (pNC->ncFlags & NC_InAggFunc)==0 |
| 112587 | 112778 | && pWalker->walkerDepth==pExpr->op2 |
| 112588 | 112779 | ){ |
| @@ -114075,10 +114266,23 @@ | ||
| 114075 | 114266 | } |
| 114076 | 114267 | |
| 114077 | 114268 | sqlite3_free(zQuot); |
| 114078 | 114269 | return rc; |
| 114079 | 114270 | } |
| 114271 | + | |
| 114272 | +/* | |
| 114273 | +** Set all pEList->a[].fg.eEName fields in the expression-list to val. | |
| 114274 | +*/ | |
| 114275 | +static void renameSetENames(ExprList *pEList, int val){ | |
| 114276 | + if( pEList ){ | |
| 114277 | + int i; | |
| 114278 | + for(i=0; i<pEList->nExpr; i++){ | |
| 114279 | + assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); | |
| 114280 | + pEList->a[i].fg.eEName = val; | |
| 114281 | + } | |
| 114282 | + } | |
| 114283 | +} | |
| 114080 | 114284 | |
| 114081 | 114285 | /* |
| 114082 | 114286 | ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming |
| 114083 | 114287 | ** it was read from the schema of database zDb. Return SQLITE_OK if |
| 114084 | 114288 | ** successful. Otherwise, return an SQLite error code and leave an error |
| @@ -114123,11 +114327,21 @@ | ||
| 114123 | 114327 | if( pSel==0 ){ |
| 114124 | 114328 | pStep->pExprList = 0; |
| 114125 | 114329 | pSrc = 0; |
| 114126 | 114330 | rc = SQLITE_NOMEM; |
| 114127 | 114331 | }else{ |
| 114332 | + /* pStep->pExprList contains an expression-list used for an UPDATE | |
| 114333 | + ** statement. So the a[].zEName values are the RHS of the | |
| 114334 | + ** "<col> = <expr>" clauses of the UPDATE statement. So, before | |
| 114335 | + ** running SelectPrep(), change all the eEName values in | |
| 114336 | + ** pStep->pExprList to ENAME_SPAN (from their current value of | |
| 114337 | + ** ENAME_NAME). This is to prevent any ids in ON() clauses that are | |
| 114338 | + ** part of pSrc from being incorrectly resolved against the | |
| 114339 | + ** a[].zEName values as if they were column aliases. */ | |
| 114340 | + renameSetENames(pStep->pExprList, ENAME_SPAN); | |
| 114128 | 114341 | sqlite3SelectPrep(pParse, pSel, 0); |
| 114342 | + renameSetENames(pStep->pExprList, ENAME_NAME); | |
| 114129 | 114343 | rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; |
| 114130 | 114344 | assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); |
| 114131 | 114345 | assert( pSrc==pSel->pSrc ); |
| 114132 | 114346 | if( pStep->pExprList ) pSel->pEList = 0; |
| 114133 | 114347 | pSel->pSrc = 0; |
| @@ -116859,17 +117073,22 @@ | ||
| 116859 | 117073 | if( zIndex==0 ) continue; |
| 116860 | 117074 | nSample = sqlite3_column_int(pStmt, 1); |
| 116861 | 117075 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 116862 | 117076 | assert( pIdx==0 || pIdx->nSample==0 ); |
| 116863 | 117077 | if( pIdx==0 ) continue; |
| 117078 | + if( pIdx->aSample!=0 ){ | |
| 117079 | + /* The same index appears in sqlite_stat4 under multiple names */ | |
| 117080 | + continue; | |
| 117081 | + } | |
| 116864 | 117082 | assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); |
| 116865 | 117083 | if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ |
| 116866 | 117084 | nIdxCol = pIdx->nKeyCol; |
| 116867 | 117085 | }else{ |
| 116868 | 117086 | nIdxCol = pIdx->nColumn; |
| 116869 | 117087 | } |
| 116870 | 117088 | pIdx->nSampleCol = nIdxCol; |
| 117089 | + pIdx->mxSample = nSample; | |
| 116871 | 117090 | nByte = sizeof(IndexSample) * nSample; |
| 116872 | 117091 | nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; |
| 116873 | 117092 | nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ |
| 116874 | 117093 | |
| 116875 | 117094 | pIdx->aSample = sqlite3DbMallocZero(db, nByte); |
| @@ -116905,10 +117124,15 @@ | ||
| 116905 | 117124 | |
| 116906 | 117125 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 116907 | 117126 | if( zIndex==0 ) continue; |
| 116908 | 117127 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 116909 | 117128 | if( pIdx==0 ) continue; |
| 117129 | + if( pIdx->nSample>=pIdx->mxSample ){ | |
| 117130 | + /* Too many slots used because the same index appears in | |
| 117131 | + ** sqlite_stat4 using multiple names */ | |
| 117132 | + continue; | |
| 117133 | + } | |
| 116910 | 117134 | /* This next condition is true if data has already been loaded from |
| 116911 | 117135 | ** the sqlite_stat4 table. */ |
| 116912 | 117136 | nCol = pIdx->nSampleCol; |
| 116913 | 117137 | if( pIdx!=pPrevIdx ){ |
| 116914 | 117138 | initAvgEq(pPrevIdx); |
| @@ -116948,15 +117172,16 @@ | ||
| 116948 | 117172 | static int loadStat4(sqlite3 *db, const char *zDb){ |
| 116949 | 117173 | int rc = SQLITE_OK; /* Result codes from subroutines */ |
| 116950 | 117174 | const Table *pStat4; |
| 116951 | 117175 | |
| 116952 | 117176 | assert( db->lookaside.bDisable ); |
| 116953 | - if( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 | |
| 117177 | + if( OptimizationEnabled(db, SQLITE_Stat4) | |
| 117178 | + && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 | |
| 116954 | 117179 | && IsOrdinaryTable(pStat4) |
| 116955 | 117180 | ){ |
| 116956 | 117181 | rc = loadStatTbl(db, |
| 116957 | - "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", | |
| 117182 | + "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", | |
| 116958 | 117183 | "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", |
| 116959 | 117184 | zDb |
| 116960 | 117185 | ); |
| 116961 | 117186 | } |
| 116962 | 117187 | return rc; |
| @@ -118796,11 +119021,11 @@ | ||
| 118796 | 119021 | } |
| 118797 | 119022 | |
| 118798 | 119023 | if( IsOrdinaryTable(pTable) ){ |
| 118799 | 119024 | sqlite3FkDelete(db, pTable); |
| 118800 | 119025 | } |
| 118801 | -#ifndef SQLITE_OMIT_VIRTUAL_TABLE | |
| 119026 | +#ifndef SQLITE_OMIT_VIRTUALTABLE | |
| 118802 | 119027 | else if( IsVirtual(pTable) ){ |
| 118803 | 119028 | sqlite3VtabClear(db, pTable); |
| 118804 | 119029 | } |
| 118805 | 119030 | #endif |
| 118806 | 119031 | else{ |
| @@ -123828,10 +124053,11 @@ | ||
| 123828 | 124053 | db->enc = enc; |
| 123829 | 124054 | /* EVIDENCE-OF: R-08308-17224 The default collating function for all |
| 123830 | 124055 | ** strings is BINARY. |
| 123831 | 124056 | */ |
| 123832 | 124057 | db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); |
| 124058 | + sqlite3ExpirePreparedStatements(db, 1); | |
| 123833 | 124059 | } |
| 123834 | 124060 | |
| 123835 | 124061 | /* |
| 123836 | 124062 | ** This function is responsible for invoking the collation factory callback |
| 123837 | 124063 | ** or substituting a collation sequence of a different encoding when the |
| @@ -126724,11 +126950,11 @@ | ||
| 126724 | 126950 | |
| 126725 | 126951 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 126726 | 126952 | /* |
| 126727 | 126953 | ** The "unknown" function is automatically substituted in place of |
| 126728 | 126954 | ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN |
| 126729 | -** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. | |
| 126955 | +** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. | |
| 126730 | 126956 | ** When the "sqlite3" command-line shell is built using this functionality, |
| 126731 | 126957 | ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries |
| 126732 | 126958 | ** involving application-defined functions to be examined in a generic |
| 126733 | 126959 | ** sqlite3 shell. |
| 126734 | 126960 | */ |
| @@ -129027,26 +129253,26 @@ | ||
| 129027 | 129253 | zFrom = pFKey->pFrom->zName; |
| 129028 | 129254 | nFrom = sqlite3Strlen30(zFrom); |
| 129029 | 129255 | |
| 129030 | 129256 | if( action==OE_Restrict ){ |
| 129031 | 129257 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 129032 | - Token tFrom; | |
| 129033 | - Token tDb; | |
| 129258 | + SrcList *pSrc; | |
| 129034 | 129259 | Expr *pRaise; |
| 129035 | 129260 | |
| 129036 | - tFrom.z = zFrom; | |
| 129037 | - tFrom.n = nFrom; | |
| 129038 | - tDb.z = db->aDb[iDb].zDbSName; | |
| 129039 | - tDb.n = sqlite3Strlen30(tDb.z); | |
| 129040 | - | |
| 129041 | 129261 | pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); |
| 129042 | 129262 | if( pRaise ){ |
| 129043 | 129263 | pRaise->affExpr = OE_Abort; |
| 129044 | 129264 | } |
| 129265 | + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); | |
| 129266 | + if( pSrc ){ | |
| 129267 | + assert( pSrc->nSrc==1 ); | |
| 129268 | + pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); | |
| 129269 | + pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); | |
| 129270 | + } | |
| 129045 | 129271 | pSelect = sqlite3SelectNew(pParse, |
| 129046 | 129272 | sqlite3ExprListAppend(pParse, 0, pRaise), |
| 129047 | - sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom), | |
| 129273 | + pSrc, | |
| 129048 | 129274 | pWhere, |
| 129049 | 129275 | 0, 0, 0, 0, 0 |
| 129050 | 129276 | ); |
| 129051 | 129277 | pWhere = 0; |
| 129052 | 129278 | } |
| @@ -137995,11 +138221,13 @@ | ||
| 137995 | 138221 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 137996 | 138222 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 137997 | 138223 | #else |
| 137998 | 138224 | encoding = SQLITE_UTF8; |
| 137999 | 138225 | #endif |
| 138000 | - if( db->nVdbeActive>0 && encoding!=ENC(db) ){ | |
| 138226 | + if( db->nVdbeActive>0 && encoding!=ENC(db) | |
| 138227 | + && (db->mDbFlags & DBFLAG_Vacuum)==0 | |
| 138228 | + ){ | |
| 138001 | 138229 | rc = SQLITE_LOCKED; |
| 138002 | 138230 | goto initone_error_out; |
| 138003 | 138231 | }else{ |
| 138004 | 138232 | sqlite3SetTextEncoding(db, encoding); |
| 138005 | 138233 | } |
| @@ -138389,11 +138617,15 @@ | ||
| 138389 | 138617 | sParse.pOuterParse = db->pParse; |
| 138390 | 138618 | db->pParse = &sParse; |
| 138391 | 138619 | sParse.db = db; |
| 138392 | 138620 | sParse.pReprepare = pReprepare; |
| 138393 | 138621 | assert( ppStmt && *ppStmt==0 ); |
| 138394 | - if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); | |
| 138622 | + if( db->mallocFailed ){ | |
| 138623 | + sqlite3ErrorMsg(&sParse, "out of memory"); | |
| 138624 | + db->errCode = rc = SQLITE_NOMEM; | |
| 138625 | + goto end_prepare; | |
| 138626 | + } | |
| 138395 | 138627 | assert( sqlite3_mutex_held(db->mutex) ); |
| 138396 | 138628 | |
| 138397 | 138629 | /* For a long-term use prepared statement avoid the use of |
| 138398 | 138630 | ** lookaside memory. |
| 138399 | 138631 | */ |
| @@ -139478,11 +139710,11 @@ | ||
| 139478 | 139710 | ** by a prior OP_MakeRecord. In this case nData==1 and regData |
| 139479 | 139711 | ** will be completely unrelated to regOrigData. |
| 139480 | 139712 | ** (2) All output columns are included in the sort record. In that |
| 139481 | 139713 | ** case regData==regOrigData. |
| 139482 | 139714 | ** (3) Some output columns are omitted from the sort record due to |
| 139483 | - ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the | |
| 139715 | + ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the | |
| 139484 | 139716 | ** SQLITE_ECEL_OMITREF optimization, or due to the |
| 139485 | 139717 | ** SortCtx.pDeferredRowLoad optimiation. In any of these cases |
| 139486 | 139718 | ** regOrigData is 0 to prevent this routine from trying to copy |
| 139487 | 139719 | ** values that might not yet exist. |
| 139488 | 139720 | */ |
| @@ -141079,11 +141311,11 @@ | ||
| 141079 | 141311 | |
| 141080 | 141312 | assert( pSelect!=0 ); |
| 141081 | 141313 | assert( (pSelect->selFlags & SF_Resolved)!=0 ); |
| 141082 | 141314 | assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); |
| 141083 | 141315 | assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); |
| 141084 | - if( db->mallocFailed ) return; | |
| 141316 | + if( db->mallocFailed || IN_RENAME_OBJECT ) return; | |
| 141085 | 141317 | while( pSelect->pPrior ) pSelect = pSelect->pPrior; |
| 141086 | 141318 | a = pSelect->pEList->a; |
| 141087 | 141319 | memset(&sNC, 0, sizeof(sNC)); |
| 141088 | 141320 | sNC.pSrcList = pSelect->pSrc; |
| 141089 | 141321 | for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ |
| @@ -141124,22 +141356,20 @@ | ||
| 141124 | 141356 | if( sqlite3StdTypeAffinity[j]==pCol->affinity ){ |
| 141125 | 141357 | zType = sqlite3StdType[j]; |
| 141126 | 141358 | break; |
| 141127 | 141359 | } |
| 141128 | 141360 | } |
| 141129 | - } | |
| 141130 | - } | |
| 141131 | - if( zType ){ | |
| 141132 | - i64 m = sqlite3Strlen30(zType); | |
| 141133 | - n = sqlite3Strlen30(pCol->zCnName); | |
| 141134 | - pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); | |
| 141135 | - if( pCol->zCnName ){ | |
| 141136 | - memcpy(&pCol->zCnName[n+1], zType, m+1); | |
| 141137 | - pCol->colFlags |= COLFLAG_HASTYPE; | |
| 141138 | - }else{ | |
| 141139 | - testcase( pCol->colFlags & COLFLAG_HASTYPE ); | |
| 141140 | - pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); | |
| 141361 | + } | |
| 141362 | + } | |
| 141363 | + if( zType ){ | |
| 141364 | + i64 m = sqlite3Strlen30(zType); | |
| 141365 | + n = sqlite3Strlen30(pCol->zCnName); | |
| 141366 | + pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); | |
| 141367 | + pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); | |
| 141368 | + if( pCol->zCnName ){ | |
| 141369 | + memcpy(&pCol->zCnName[n+1], zType, m+1); | |
| 141370 | + pCol->colFlags |= COLFLAG_HASTYPE; | |
| 141141 | 141371 | } |
| 141142 | 141372 | } |
| 141143 | 141373 | pColl = sqlite3ExprCollSeq(pParse, p); |
| 141144 | 141374 | if( pColl ){ |
| 141145 | 141375 | assert( pTab->pIndex==0 ); |
| @@ -143870,25 +144100,50 @@ | ||
| 143870 | 144100 | ** |
| 143871 | 144101 | ** (8) If the subquery is a compound that uses UNION, INTERSECT, |
| 143872 | 144102 | ** or EXCEPT, then all of the result set columns for all arms of |
| 143873 | 144103 | ** the compound must use the BINARY collating sequence. |
| 143874 | 144104 | ** |
| 144105 | +** (9) All three of the following are true: | |
| 144106 | +** | |
| 144107 | +** (9a) The WHERE clause expression originates in the ON or USING clause | |
| 144108 | +** of a join (either an INNER or an OUTER join), and | |
| 144109 | +** | |
| 144110 | +** (9b) The subquery is to the right of the ON/USING clause | |
| 144111 | +** | |
| 144112 | +** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING | |
| 144113 | +** clause and the subquery. | |
| 144114 | +** | |
| 144115 | +** Without this restriction, the push-down optimization might move | |
| 144116 | +** the ON/USING filter expression from the left side of a RIGHT JOIN | |
| 144117 | +** over to the right side, which leads to incorrect answers. See | |
| 144118 | +** also restriction (6) in sqlite3ExprIsSingleTableConstraint(). | |
| 144119 | +** | |
| 144120 | +** (10) The inner query is not the right-hand table of a RIGHT JOIN. | |
| 144121 | +** | |
| 144122 | +** (11) The subquery is not a VALUES clause | |
| 143875 | 144123 | ** |
| 143876 | 144124 | ** Return 0 if no changes are made and non-zero if one or more WHERE clause |
| 143877 | 144125 | ** terms are duplicated into the subquery. |
| 143878 | 144126 | */ |
| 143879 | 144127 | static int pushDownWhereTerms( |
| 143880 | 144128 | Parse *pParse, /* Parse context (for malloc() and error reporting) */ |
| 143881 | 144129 | Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ |
| 143882 | 144130 | Expr *pWhere, /* The WHERE clause of the outer query */ |
| 143883 | - SrcItem *pSrc /* The subquery term of the outer FROM clause */ | |
| 144131 | + SrcList *pSrcList, /* The complete from clause of the outer query */ | |
| 144132 | + int iSrc /* Which FROM clause term to try to push into */ | |
| 143884 | 144133 | ){ |
| 143885 | 144134 | Expr *pNew; |
| 144135 | + SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */ | |
| 143886 | 144136 | int nChng = 0; |
| 144137 | + pSrc = &pSrcList->a[iSrc]; | |
| 143887 | 144138 | if( pWhere==0 ) return 0; |
| 143888 | - if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; | |
| 143889 | - if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0; | |
| 144139 | + if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){ | |
| 144140 | + return 0; /* restrictions (2) and (11) */ | |
| 144141 | + } | |
| 144142 | + if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){ | |
| 144143 | + return 0; /* restrictions (10) */ | |
| 144144 | + } | |
| 143890 | 144145 | |
| 143891 | 144146 | if( pSubq->pPrior ){ |
| 143892 | 144147 | Select *pSel; |
| 143893 | 144148 | int notUnionAll = 0; |
| 143894 | 144149 | for(pSel=pSubq; pSel; pSel=pSel->pPrior){ |
| @@ -143939,15 +144194,32 @@ | ||
| 143939 | 144194 | |
| 143940 | 144195 | if( pSubq->pLimit!=0 ){ |
| 143941 | 144196 | return 0; /* restriction (3) */ |
| 143942 | 144197 | } |
| 143943 | 144198 | while( pWhere->op==TK_AND ){ |
| 143944 | - nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); | |
| 144199 | + nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc); | |
| 143945 | 144200 | pWhere = pWhere->pLeft; |
| 143946 | 144201 | } |
| 143947 | 144202 | |
| 143948 | -#if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */ | |
| 144203 | +#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */ | |
| 144204 | + if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */ | |
| 144205 | + && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */ | |
| 144206 | + ){ | |
| 144207 | + int jj; | |
| 144208 | + for(jj=0; jj<iSrc; jj++){ | |
| 144209 | + if( pWhere->w.iJoin==pSrcList->a[jj].iCursor ){ | |
| 144210 | + /* If we reach this point, both (9a) and (9b) are satisfied. | |
| 144211 | + ** The following loop checks (9c): | |
| 144212 | + */ | |
| 144213 | + for(jj++; jj<iSrc; jj++){ | |
| 144214 | + if( (pSrcList->a[jj].fg.jointype & JT_RIGHT)!=0 ){ | |
| 144215 | + return 0; /* restriction (9) */ | |
| 144216 | + } | |
| 144217 | + } | |
| 144218 | + } | |
| 144219 | + } | |
| 144220 | + } | |
| 143949 | 144221 | if( isLeftJoin |
| 143950 | 144222 | && (ExprHasProperty(pWhere,EP_OuterON)==0 |
| 143951 | 144223 | || pWhere->w.iJoin!=iCursor) |
| 143952 | 144224 | ){ |
| 143953 | 144225 | return 0; /* restriction (4) */ |
| @@ -143957,11 +144229,11 @@ | ||
| 143957 | 144229 | ){ |
| 143958 | 144230 | return 0; /* restriction (5) */ |
| 143959 | 144231 | } |
| 143960 | 144232 | #endif |
| 143961 | 144233 | |
| 143962 | - if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ | |
| 144234 | + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ | |
| 143963 | 144235 | nChng++; |
| 143964 | 144236 | pSubq->selFlags |= SF_PushDown; |
| 143965 | 144237 | while( pSubq ){ |
| 143966 | 144238 | SubstContext x; |
| 143967 | 144239 | pNew = sqlite3ExprDup(pParse->db, pWhere, 0); |
| @@ -144027,15 +144299,17 @@ | ||
| 144027 | 144299 | if( pX->pPrior && pX->op!=TK_ALL ){ |
| 144028 | 144300 | /* This optimization does not work for compound subqueries that |
| 144029 | 144301 | ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ |
| 144030 | 144302 | return 0; |
| 144031 | 144303 | } |
| 144304 | +#ifndef SQLITE_OMIT_WINDOWFUNC | |
| 144032 | 144305 | if( pX->pWin ){ |
| 144033 | 144306 | /* This optimization does not work for subqueries that use window |
| 144034 | 144307 | ** functions. */ |
| 144035 | 144308 | return 0; |
| 144036 | 144309 | } |
| 144310 | +#endif | |
| 144037 | 144311 | } |
| 144038 | 144312 | colUsed = pItem->colUsed; |
| 144039 | 144313 | if( pSub->pOrderBy ){ |
| 144040 | 144314 | ExprList *pList = pSub->pOrderBy; |
| 144041 | 144315 | for(j=0; j<pList->nExpr; j++){ |
| @@ -145207,16 +145481,17 @@ | ||
| 145207 | 145481 | assert( pAggInfo->iFirstReg==0 ); |
| 145208 | 145482 | assert( pSelect!=0 ); |
| 145209 | 145483 | assert( pSelect->pGroupBy!=0 ); |
| 145210 | 145484 | pAggInfo->nColumn = pAggInfo->nAccumulator; |
| 145211 | 145485 | if( ALWAYS(pAggInfo->nSortingColumn>0) ){ |
| 145212 | - if( pAggInfo->nColumn==0 ){ | |
| 145213 | - pAggInfo->nSortingColumn = pSelect->pGroupBy->nExpr; | |
| 145214 | - }else{ | |
| 145215 | - pAggInfo->nSortingColumn = | |
| 145216 | - pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1; | |
| 145486 | + int mx = pSelect->pGroupBy->nExpr - 1; | |
| 145487 | + int j, k; | |
| 145488 | + for(j=0; j<pAggInfo->nColumn; j++){ | |
| 145489 | + k = pAggInfo->aCol[j].iSorterColumn; | |
| 145490 | + if( k>mx ) mx = k; | |
| 145217 | 145491 | } |
| 145492 | + pAggInfo->nSortingColumn = mx+1; | |
| 145218 | 145493 | } |
| 145219 | 145494 | analyzeAggFuncArgs(pAggInfo, pNC); |
| 145220 | 145495 | #if TREETRACE_ENABLED |
| 145221 | 145496 | if( sqlite3TreeTrace & 0x20 ){ |
| 145222 | 145497 | IndexedExpr *pIEpr; |
| @@ -145952,11 +146227,11 @@ | ||
| 145952 | 146227 | assert( pItem->iCursor>=0 ); |
| 145953 | 146228 | unsetJoinExpr(p->pWhere, pItem->iCursor, |
| 145954 | 146229 | pTabList->a[0].fg.jointype & JT_LTORJ); |
| 145955 | 146230 | } |
| 145956 | 146231 | |
| 145957 | - /* No futher action if this term of the FROM clause is no a subquery */ | |
| 146232 | + /* No futher action if this term of the FROM clause is not a subquery */ | |
| 145958 | 146233 | if( pSub==0 ) continue; |
| 145959 | 146234 | |
| 145960 | 146235 | /* Catch mismatch in the declared columns of a view and the number of |
| 145961 | 146236 | ** columns in the SELECT on the RHS */ |
| 145962 | 146237 | if( pTab->nCol!=pSub->pEList->nExpr ){ |
| @@ -146149,11 +146424,11 @@ | ||
| 146149 | 146424 | ** inside the subquery. This can help the subquery to run more efficiently. |
| 146150 | 146425 | */ |
| 146151 | 146426 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 146152 | 146427 | && (pItem->fg.isCte==0 |
| 146153 | 146428 | || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) |
| 146154 | - && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem) | |
| 146429 | + && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i) | |
| 146155 | 146430 | ){ |
| 146156 | 146431 | #if TREETRACE_ENABLED |
| 146157 | 146432 | if( sqlite3TreeTrace & 0x4000 ){ |
| 146158 | 146433 | TREETRACE(0x4000,pParse,p, |
| 146159 | 146434 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| @@ -151944,11 +152219,14 @@ | ||
| 151944 | 152219 | default: |
| 151945 | 152220 | xMethod = pMod->xRelease; |
| 151946 | 152221 | break; |
| 151947 | 152222 | } |
| 151948 | 152223 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 152224 | + u64 savedFlags = (db->flags & SQLITE_Defensive); | |
| 152225 | + db->flags &= ~(u64)SQLITE_Defensive; | |
| 151949 | 152226 | rc = xMethod(pVTab->pVtab, iSavepoint); |
| 152227 | + db->flags |= savedFlags; | |
| 151950 | 152228 | } |
| 151951 | 152229 | sqlite3VtabUnlock(pVTab); |
| 151952 | 152230 | } |
| 151953 | 152231 | } |
| 151954 | 152232 | } |
| @@ -153869,10 +154147,13 @@ | ||
| 153869 | 154147 | rc = WRC_Prune; |
| 153870 | 154148 | reg = ++pWalker->pParse->nMem; /* Register for column value */ |
| 153871 | 154149 | reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); |
| 153872 | 154150 | pExpr->op = TK_REGISTER; |
| 153873 | 154151 | pExpr->iTable = reg; |
| 154152 | + }else if( pExpr->op==TK_TRUEFALSE ){ | |
| 154153 | + /* Do not walk disabled expressions. tag-20230504-1 */ | |
| 154154 | + return WRC_Prune; | |
| 153874 | 154155 | } |
| 153875 | 154156 | return rc; |
| 153876 | 154157 | } |
| 153877 | 154158 | |
| 153878 | 154159 | /* |
| @@ -153970,11 +154251,11 @@ | ||
| 153970 | 154251 | /* If we survive all prior tests, that means this term is worth hinting */ |
| 153971 | 154252 | pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0)); |
| 153972 | 154253 | } |
| 153973 | 154254 | if( pExpr!=0 ){ |
| 153974 | 154255 | sWalker.xExprCallback = codeCursorHintFixExpr; |
| 153975 | - sqlite3WalkExpr(&sWalker, pExpr); | |
| 154256 | + if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr); | |
| 153976 | 154257 | sqlite3VdbeAddOp4(v, OP_CursorHint, |
| 153977 | 154258 | (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, |
| 153978 | 154259 | (const char*)pExpr, P4_EXPR); |
| 153979 | 154260 | } |
| 153980 | 154261 | } |
| @@ -156836,11 +157117,11 @@ | ||
| 156836 | 157117 | if( op==TK_ISNULL |
| 156837 | 157118 | && !ExprHasProperty(pExpr,EP_OuterON) |
| 156838 | 157119 | && 0==sqlite3ExprCanBeNull(pLeft) |
| 156839 | 157120 | ){ |
| 156840 | 157121 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 156841 | - pExpr->op = TK_TRUEFALSE; | |
| 157122 | + pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */ | |
| 156842 | 157123 | pExpr->u.zToken = "false"; |
| 156843 | 157124 | ExprSetProperty(pExpr, EP_IsFalse); |
| 156844 | 157125 | pTerm->prereqAll = 0; |
| 156845 | 157126 | pTerm->eOperator = 0; |
| 156846 | 157127 | } |
| @@ -158368,12 +158649,11 @@ | ||
| 158368 | 158649 | ** and to set up the WhereLevel object pLevel so that the code generator |
| 158369 | 158650 | ** makes use of the automatic index. |
| 158370 | 158651 | */ |
| 158371 | 158652 | static SQLITE_NOINLINE void constructAutomaticIndex( |
| 158372 | 158653 | Parse *pParse, /* The parsing context */ |
| 158373 | - const WhereClause *pWC, /* The WHERE clause */ | |
| 158374 | - const SrcItem *pSrc, /* The FROM clause term to get the next index */ | |
| 158654 | + WhereClause *pWC, /* The WHERE clause */ | |
| 158375 | 158655 | const Bitmask notReady, /* Mask of cursors that are not available */ |
| 158376 | 158656 | WhereLevel *pLevel /* Write new index here */ |
| 158377 | 158657 | ){ |
| 158378 | 158658 | int nKeyCol; /* Number of columns in the constructed index */ |
| 158379 | 158659 | WhereTerm *pTerm; /* A single term of the WHERE clause */ |
| @@ -158394,11 +158674,12 @@ | ||
| 158394 | 158674 | Bitmask extraCols; /* Bitmap of additional columns */ |
| 158395 | 158675 | u8 sentWarning = 0; /* True if a warning has been issued */ |
| 158396 | 158676 | u8 useBloomFilter = 0; /* True to also add a Bloom filter */ |
| 158397 | 158677 | Expr *pPartial = 0; /* Partial Index Expression */ |
| 158398 | 158678 | int iContinue = 0; /* Jump here to skip excluded rows */ |
| 158399 | - SrcItem *pTabItem; /* FROM clause term being indexed */ | |
| 158679 | + SrcList *pTabList; /* The complete FROM clause */ | |
| 158680 | + SrcItem *pSrc; /* The FROM clause term to get the next index */ | |
| 158400 | 158681 | int addrCounter = 0; /* Address where integer counter is initialized */ |
| 158401 | 158682 | int regBase; /* Array of registers where record is assembled */ |
| 158402 | 158683 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158403 | 158684 | int addrExp = 0; /* Address of OP_Explain */ |
| 158404 | 158685 | #endif |
| @@ -158410,10 +158691,12 @@ | ||
| 158410 | 158691 | addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158411 | 158692 | |
| 158412 | 158693 | /* Count the number of columns that will be added to the index |
| 158413 | 158694 | ** and used to match WHERE clause constraints */ |
| 158414 | 158695 | nKeyCol = 0; |
| 158696 | + pTabList = pWC->pWInfo->pTabList; | |
| 158697 | + pSrc = &pTabList->a[pLevel->iFrom]; | |
| 158415 | 158698 | pTable = pSrc->pTab; |
| 158416 | 158699 | pWCEnd = &pWC->a[pWC->nTerm]; |
| 158417 | 158700 | pLoop = pLevel->pWLoop; |
| 158418 | 158701 | idxCols = 0; |
| 158419 | 158702 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| @@ -158420,11 +158703,11 @@ | ||
| 158420 | 158703 | Expr *pExpr = pTerm->pExpr; |
| 158421 | 158704 | /* Make the automatic index a partial index if there are terms in the |
| 158422 | 158705 | ** WHERE clause (or the ON clause of a LEFT join) that constrain which |
| 158423 | 158706 | ** rows of the target table (pSrc) that can be used. */ |
| 158424 | 158707 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158425 | - && sqlite3ExprIsTableConstraint(pExpr, pSrc) | |
| 158708 | + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) | |
| 158426 | 158709 | ){ |
| 158427 | 158710 | pPartial = sqlite3ExprAnd(pParse, pPartial, |
| 158428 | 158711 | sqlite3ExprDup(pParse->db, pExpr, 0)); |
| 158429 | 158712 | } |
| 158430 | 158713 | if( termCanDriveIndex(pTerm, pSrc, notReady) ){ |
| @@ -158550,18 +158833,18 @@ | ||
| 158550 | 158833 | pLevel->regFilter = ++pParse->nMem; |
| 158551 | 158834 | sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); |
| 158552 | 158835 | } |
| 158553 | 158836 | |
| 158554 | 158837 | /* Fill the automatic index with content */ |
| 158555 | - pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; | |
| 158556 | - if( pTabItem->fg.viaCoroutine ){ | |
| 158557 | - int regYield = pTabItem->regReturn; | |
| 158838 | + assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); | |
| 158839 | + if( pSrc->fg.viaCoroutine ){ | |
| 158840 | + int regYield = pSrc->regReturn; | |
| 158558 | 158841 | addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); |
| 158559 | - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); | |
| 158842 | + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub); | |
| 158560 | 158843 | addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); |
| 158561 | 158844 | VdbeCoverage(v); |
| 158562 | - VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); | |
| 158845 | + VdbeComment((v, "next row of %s", pSrc->pTab->zName)); | |
| 158563 | 158846 | }else{ |
| 158564 | 158847 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); |
| 158565 | 158848 | } |
| 158566 | 158849 | if( pPartial ){ |
| 158567 | 158850 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| @@ -158578,18 +158861,18 @@ | ||
| 158578 | 158861 | } |
| 158579 | 158862 | sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v)); |
| 158580 | 158863 | sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); |
| 158581 | 158864 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); |
| 158582 | 158865 | if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); |
| 158583 | - if( pTabItem->fg.viaCoroutine ){ | |
| 158866 | + if( pSrc->fg.viaCoroutine ){ | |
| 158584 | 158867 | sqlite3VdbeChangeP2(v, addrCounter, regBase+n); |
| 158585 | 158868 | testcase( pParse->db->mallocFailed ); |
| 158586 | 158869 | assert( pLevel->iIdxCur>0 ); |
| 158587 | 158870 | translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, |
| 158588 | - pTabItem->regResult, pLevel->iIdxCur); | |
| 158871 | + pSrc->regResult, pLevel->iIdxCur); | |
| 158589 | 158872 | sqlite3VdbeGoto(v, addrTop); |
| 158590 | - pTabItem->fg.viaCoroutine = 0; | |
| 158873 | + pSrc->fg.viaCoroutine = 0; | |
| 158591 | 158874 | }else{ |
| 158592 | 158875 | sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); |
| 158593 | 158876 | sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); |
| 158594 | 158877 | } |
| 158595 | 158878 | sqlite3VdbeJumpHere(v, addrTop); |
| @@ -158648,13 +158931,15 @@ | ||
| 158648 | 158931 | assert( v!=0 ); |
| 158649 | 158932 | assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); |
| 158650 | 158933 | |
| 158651 | 158934 | addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158652 | 158935 | do{ |
| 158936 | + const SrcList *pTabList; | |
| 158653 | 158937 | const SrcItem *pItem; |
| 158654 | 158938 | const Table *pTab; |
| 158655 | 158939 | u64 sz; |
| 158940 | + int iSrc; | |
| 158656 | 158941 | sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); |
| 158657 | 158942 | addrCont = sqlite3VdbeMakeLabel(pParse); |
| 158658 | 158943 | iCur = pLevel->iTabCur; |
| 158659 | 158944 | pLevel->regFilter = ++pParse->nMem; |
| 158660 | 158945 | |
| @@ -158664,11 +158949,13 @@ | ||
| 158664 | 158949 | ** measure the size of the table at run-time using OP_Count with |
| 158665 | 158950 | ** P3==1 and use that value to initialize the blob. But that makes |
| 158666 | 158951 | ** testing complicated. By basing the blob size on the value in the |
| 158667 | 158952 | ** sqlite_stat1 table, testing is much easier. |
| 158668 | 158953 | */ |
| 158669 | - pItem = &pWInfo->pTabList->a[pLevel->iFrom]; | |
| 158954 | + pTabList = pWInfo->pTabList; | |
| 158955 | + iSrc = pLevel->iFrom; | |
| 158956 | + pItem = &pTabList->a[iSrc]; | |
| 158670 | 158957 | assert( pItem!=0 ); |
| 158671 | 158958 | pTab = pItem->pTab; |
| 158672 | 158959 | assert( pTab!=0 ); |
| 158673 | 158960 | sz = sqlite3LogEstToInt(pTab->nRowLogEst); |
| 158674 | 158961 | if( sz<10000 ){ |
| @@ -158681,11 +158968,11 @@ | ||
| 158681 | 158968 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); |
| 158682 | 158969 | pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; |
| 158683 | 158970 | for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){ |
| 158684 | 158971 | Expr *pExpr = pTerm->pExpr; |
| 158685 | 158972 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158686 | - && sqlite3ExprIsTableConstraint(pExpr, pItem) | |
| 158973 | + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) | |
| 158687 | 158974 | ){ |
| 158688 | 158975 | sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); |
| 158689 | 158976 | } |
| 158690 | 158977 | } |
| 158691 | 158978 | if( pLoop->wsFlags & WHERE_IPK ){ |
| @@ -163020,10 +163307,17 @@ | ||
| 163020 | 163307 | ** 3) Either the query must be DISTINCT, or else the ON or USING clause |
| 163021 | 163308 | ** must contain a constraint that limits the scan of the table to |
| 163022 | 163309 | ** at most a single row. |
| 163023 | 163310 | ** 4) The table must not be referenced by any part of the query apart |
| 163024 | 163311 | ** from its own USING or ON clause. |
| 163312 | +** 5) The table must not have an inner-join ON or USING clause if there is | |
| 163313 | +** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause | |
| 163314 | +** might move from the right side to the left side of the RIGHT JOIN. | |
| 163315 | +** Note: Due to (2), this condition can only arise if the table is | |
| 163316 | +** the right-most table of a subquery that was flattened into the | |
| 163317 | +** main query and that subquery was the right-hand operand of an | |
| 163318 | +** inner join that held an ON or USING clause. | |
| 163025 | 163319 | ** |
| 163026 | 163320 | ** For example, given: |
| 163027 | 163321 | ** |
| 163028 | 163322 | ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); |
| 163029 | 163323 | ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); |
| @@ -163045,10 +163339,11 @@ | ||
| 163045 | 163339 | WhereInfo *pWInfo, |
| 163046 | 163340 | Bitmask notReady |
| 163047 | 163341 | ){ |
| 163048 | 163342 | int i; |
| 163049 | 163343 | Bitmask tabUsed; |
| 163344 | + int hasRightJoin; | |
| 163050 | 163345 | |
| 163051 | 163346 | /* Preconditions checked by the caller */ |
| 163052 | 163347 | assert( pWInfo->nLevel>=2 ); |
| 163053 | 163348 | assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); |
| 163054 | 163349 | |
| @@ -163059,10 +163354,11 @@ | ||
| 163059 | 163354 | |
| 163060 | 163355 | tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); |
| 163061 | 163356 | if( pWInfo->pOrderBy ){ |
| 163062 | 163357 | tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); |
| 163063 | 163358 | } |
| 163359 | + hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; | |
| 163064 | 163360 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 163065 | 163361 | WhereTerm *pTerm, *pEnd; |
| 163066 | 163362 | SrcItem *pItem; |
| 163067 | 163363 | WhereLoop *pLoop; |
| 163068 | 163364 | pLoop = pWInfo->a[i].pWLoop; |
| @@ -163081,10 +163377,16 @@ | ||
| 163081 | 163377 | || pTerm->pExpr->w.iJoin!=pItem->iCursor |
| 163082 | 163378 | ){ |
| 163083 | 163379 | break; |
| 163084 | 163380 | } |
| 163085 | 163381 | } |
| 163382 | + if( hasRightJoin | |
| 163383 | + && ExprHasProperty(pTerm->pExpr, EP_InnerON) | |
| 163384 | + && pTerm->pExpr->w.iJoin==pItem->iCursor | |
| 163385 | + ){ | |
| 163386 | + break; /* restriction (5) */ | |
| 163387 | + } | |
| 163086 | 163388 | } |
| 163087 | 163389 | if( pTerm<pEnd ) continue; |
| 163088 | 163390 | WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId)); |
| 163089 | 163391 | notReady &= ~pLoop->maskSelf; |
| 163090 | 163392 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| @@ -163480,26 +163782,49 @@ | ||
| 163480 | 163782 | if( pSelect && pSelect->pLimit ){ |
| 163481 | 163783 | sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); |
| 163482 | 163784 | } |
| 163483 | 163785 | if( pParse->nErr ) goto whereBeginError; |
| 163484 | 163786 | |
| 163485 | - /* Special case: WHERE terms that do not refer to any tables in the join | |
| 163486 | - ** (constant expressions). Evaluate each such term, and jump over all the | |
| 163487 | - ** generated code if the result is not true. | |
| 163488 | - ** | |
| 163489 | - ** Do not do this if the expression contains non-deterministic functions | |
| 163490 | - ** that are not within a sub-select. This is not strictly required, but | |
| 163491 | - ** preserves SQLite's legacy behaviour in the following two cases: | |
| 163492 | - ** | |
| 163493 | - ** FROM ... WHERE random()>0; -- eval random() once per row | |
| 163494 | - ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall | |
| 163787 | + /* The False-WHERE-Term-Bypass optimization: | |
| 163788 | + ** | |
| 163789 | + ** If there are WHERE terms that are false, then no rows will be output, | |
| 163790 | + ** so skip over all of the code generated here. | |
| 163791 | + ** | |
| 163792 | + ** Conditions: | |
| 163793 | + ** | |
| 163794 | + ** (1) The WHERE term must not refer to any tables in the join. | |
| 163795 | + ** (2) The term must not come from an ON clause on the | |
| 163796 | + ** right-hand side of a LEFT or FULL JOIN. | |
| 163797 | + ** (3) The term must not come from an ON clause, or there must be | |
| 163798 | + ** no RIGHT or FULL OUTER joins in pTabList. | |
| 163799 | + ** (4) If the expression contains non-deterministic functions | |
| 163800 | + ** that are not within a sub-select. This is not required | |
| 163801 | + ** for correctness but rather to preserves SQLite's legacy | |
| 163802 | + ** behaviour in the following two cases: | |
| 163803 | + ** | |
| 163804 | + ** WHERE random()>0; -- eval random() once per row | |
| 163805 | + ** WHERE (SELECT random())>0; -- eval random() just once overall | |
| 163806 | + ** | |
| 163807 | + ** Note that the Where term need not be a constant in order for this | |
| 163808 | + ** optimization to apply, though it does need to be constant relative to | |
| 163809 | + ** the current subquery (condition 1). The term might include variables | |
| 163810 | + ** from outer queries so that the value of the term changes from one | |
| 163811 | + ** invocation of the current subquery to the next. | |
| 163495 | 163812 | */ |
| 163496 | 163813 | for(ii=0; ii<sWLB.pWC->nBase; ii++){ |
| 163497 | - WhereTerm *pT = &sWLB.pWC->a[ii]; | |
| 163814 | + WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */ | |
| 163815 | + Expr *pX; /* The expression of pT */ | |
| 163498 | 163816 | if( pT->wtFlags & TERM_VIRTUAL ) continue; |
| 163499 | - if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ | |
| 163500 | - sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); | |
| 163817 | + pX = pT->pExpr; | |
| 163818 | + assert( pX!=0 ); | |
| 163819 | + assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) ); | |
| 163820 | + if( pT->prereqAll==0 /* Conditions (1) and (2) */ | |
| 163821 | + && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */ | |
| 163822 | + && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */ | |
| 163823 | + && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 ) | |
| 163824 | + ){ | |
| 163825 | + sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL); | |
| 163501 | 163826 | pT->wtFlags |= TERM_CODED; |
| 163502 | 163827 | } |
| 163503 | 163828 | } |
| 163504 | 163829 | |
| 163505 | 163830 | if( wctrlFlags & WHERE_WANT_DISTINCT ){ |
| @@ -163875,15 +164200,15 @@ | ||
| 163875 | 164200 | int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 163876 | 164201 | sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); |
| 163877 | 164202 | sqlite3VdbeJumpHere(v, iOnce); |
| 163878 | 164203 | } |
| 163879 | 164204 | } |
| 164205 | + assert( pTabList == pWInfo->pTabList ); | |
| 163880 | 164206 | if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ |
| 163881 | 164207 | if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ |
| 163882 | 164208 | #ifndef SQLITE_OMIT_AUTOMATIC_INDEX |
| 163883 | - constructAutomaticIndex(pParse, &pWInfo->sWC, | |
| 163884 | - &pTabList->a[pLevel->iFrom], notReady, pLevel); | |
| 164209 | + constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel); | |
| 163885 | 164210 | #endif |
| 163886 | 164211 | }else{ |
| 163887 | 164212 | sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); |
| 163888 | 164213 | } |
| 163889 | 164214 | if( db->mallocFailed ) goto whereBeginError; |
| @@ -165072,10 +165397,11 @@ | ||
| 165072 | 165397 | } |
| 165073 | 165398 | } |
| 165074 | 165399 | } |
| 165075 | 165400 | /* no break */ deliberate_fall_through |
| 165076 | 165401 | |
| 165402 | + case TK_IF_NULL_ROW: | |
| 165077 | 165403 | case TK_AGG_FUNCTION: |
| 165078 | 165404 | case TK_COLUMN: { |
| 165079 | 165405 | int iCol = -1; |
| 165080 | 165406 | if( pParse->db->mallocFailed ) return WRC_Abort; |
| 165081 | 165407 | if( p->pSub ){ |
| @@ -167902,11 +168228,11 @@ | ||
| 167902 | 168228 | #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; |
| 167903 | 168229 | #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; |
| 167904 | 168230 | #define YYFALLBACK 1 |
| 167905 | 168231 | #define YYNSTATE 575 |
| 167906 | 168232 | #define YYNRULE 403 |
| 167907 | -#define YYNRULE_WITH_ACTION 341 | |
| 168233 | +#define YYNRULE_WITH_ACTION 340 | |
| 167908 | 168234 | #define YYNTOKEN 185 |
| 167909 | 168235 | #define YY_MAX_SHIFT 574 |
| 167910 | 168236 | #define YY_MIN_SHIFTREDUCE 833 |
| 167911 | 168237 | #define YY_MAX_SHIFTREDUCE 1235 |
| 167912 | 168238 | #define YY_ERROR_ACTION 1236 |
| @@ -167982,145 +168308,145 @@ | ||
| 167982 | 168308 | *********** Begin parsing tables **********************************************/ |
| 167983 | 168309 | #define YY_ACTTAB_COUNT (2096) |
| 167984 | 168310 | static const YYACTIONTYPE yy_action[] = { |
| 167985 | 168311 | /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, |
| 167986 | 168312 | /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, |
| 167987 | - /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1521, 71, | |
| 168313 | + /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71, | |
| 167988 | 168314 | /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, |
| 167989 | - /* 40 */ 397, 71, 71, 125, 126, 80, 1213, 1213, 1047, 1050, | |
| 168315 | + /* 40 */ 397, 71, 71, 125, 126, 80, 1212, 1212, 1047, 1050, | |
| 167990 | 168316 | /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, |
| 167991 | 168317 | /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, |
| 167992 | 168318 | /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, |
| 167993 | - /* 80 */ 417, 523, 142, 125, 126, 80, 1213, 1213, 1047, 1050, | |
| 168319 | + /* 80 */ 417, 523, 142, 125, 126, 80, 1212, 1212, 1047, 1050, | |
| 167994 | 168320 | /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, |
| 167995 | 168321 | /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, |
| 167996 | 168322 | /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, |
| 167997 | - /* 120 */ 442, 1562, 376, 1564, 1189, 375, 1160, 565, 1160, 565, | |
| 167998 | - /* 130 */ 409, 1562, 537, 259, 226, 444, 101, 145, 449, 316, | |
| 168323 | + /* 120 */ 442, 1561, 376, 1563, 1188, 375, 1159, 565, 1159, 565, | |
| 168324 | + /* 130 */ 409, 1561, 537, 259, 226, 444, 101, 145, 449, 316, | |
| 167999 | 168325 | /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, |
| 168000 | - /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1213, 1213, 1047, | |
| 168326 | + /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1212, 1212, 1047, | |
| 168001 | 168327 | /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, |
| 168002 | - /* 170 */ 294, 1189, 339, 448, 120, 120, 120, 119, 116, 444, | |
| 168003 | - /* 180 */ 127, 1189, 1190, 1189, 148, 441, 440, 568, 119, 116, | |
| 168328 | + /* 170 */ 294, 1188, 339, 448, 120, 120, 120, 119, 116, 444, | |
| 168329 | + /* 180 */ 127, 1188, 1189, 1188, 148, 441, 440, 568, 119, 116, | |
| 168004 | 168330 | /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, |
| 168005 | 168331 | /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, |
| 168006 | 168332 | /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, |
| 168007 | - /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1189, 1190, | |
| 168008 | - /* 230 */ 1189, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, | |
| 168333 | + /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1188, 1189, | |
| 168334 | + /* 230 */ 1188, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, | |
| 168009 | 168335 | /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168010 | 168336 | /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, |
| 168011 | - /* 260 */ 1213, 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, | |
| 168012 | - /* 270 */ 124, 124, 1275, 522, 222, 1189, 568, 409, 224, 514, | |
| 168337 | + /* 260 */ 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, | |
| 168338 | + /* 270 */ 124, 124, 1275, 522, 222, 1188, 568, 409, 224, 514, | |
| 168013 | 168339 | /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, |
| 168014 | - /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1189, 133, | |
| 168015 | - /* 300 */ 133, 125, 126, 80, 1213, 1213, 1047, 1050, 1037, 1037, | |
| 168340 | + /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1188, 133, | |
| 168341 | + /* 300 */ 133, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, | |
| 168016 | 168342 | /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, |
| 168017 | 168343 | /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, |
| 168018 | - /* 330 */ 1189, 373, 1189, 1190, 1189, 252, 1429, 399, 504, 501, | |
| 168344 | + /* 330 */ 1188, 373, 1188, 1189, 1188, 252, 1429, 399, 504, 501, | |
| 168019 | 168345 | /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, |
| 168020 | - /* 350 */ 460, 328, 360, 394, 1233, 1189, 1190, 1189, 563, 568, | |
| 168346 | + /* 350 */ 460, 328, 360, 394, 1233, 1188, 1189, 1188, 563, 568, | |
| 168021 | 168347 | /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, |
| 168022 | - /* 370 */ 116, 444, 284, 284, 369, 1575, 1601, 441, 440, 154, | |
| 168023 | - /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1189, 1190, 1189, | |
| 168024 | - /* 390 */ 85, 1219, 271, 557, 543, 515, 1556, 568, 98, 1218, | |
| 168025 | - /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1213, 1213, 1047, | |
| 168348 | + /* 370 */ 116, 444, 284, 284, 369, 1574, 1600, 441, 440, 154, | |
| 168349 | + /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1188, 1189, 1188, | |
| 168350 | + /* 390 */ 85, 1219, 271, 557, 543, 515, 1555, 568, 98, 1218, | |
| 168351 | + /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1212, 1212, 1047, | |
| 168026 | 168352 | /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, |
| 168027 | - /* 420 */ 13, 13, 1024, 507, 1220, 1189, 1220, 549, 109, 109, | |
| 168353 | + /* 420 */ 13, 13, 1024, 507, 1220, 1188, 1220, 549, 109, 109, | |
| 168028 | 168354 | /* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569, |
| 168029 | - /* 440 */ 445, 430, 1547, 1014, 325, 551, 1189, 270, 287, 368, | |
| 168355 | + /* 440 */ 445, 430, 1546, 1014, 325, 551, 1188, 270, 287, 368, | |
| 168030 | 168356 | /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, |
| 168031 | - /* 460 */ 316, 559, 1607, 122, 122, 122, 122, 121, 121, 120, | |
| 168357 | + /* 460 */ 316, 559, 1606, 122, 122, 122, 122, 121, 121, 120, | |
| 168032 | 168358 | /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, |
| 168033 | - /* 480 */ 284, 284, 1189, 1190, 1189, 1155, 568, 1606, 409, 899, | |
| 168034 | - /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1155, 516, | |
| 168035 | - /* 500 */ 413, 1155, 552, 1189, 1190, 1189, 568, 544, 1549, 51, | |
| 168036 | - /* 510 */ 51, 214, 125, 126, 80, 1213, 1213, 1047, 1050, 1037, | |
| 168037 | - /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1189, 474, 135, | |
| 168038 | - /* 530 */ 135, 409, 284, 284, 1485, 505, 121, 121, 120, 120, | |
| 168039 | - /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1556, | |
| 168040 | - /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1213, 1213, | |
| 168359 | + /* 480 */ 284, 284, 1188, 1189, 1188, 1154, 568, 1605, 409, 899, | |
| 168360 | + /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1154, 516, | |
| 168361 | + /* 500 */ 413, 1154, 552, 1188, 1189, 1188, 568, 544, 1548, 51, | |
| 168362 | + /* 510 */ 51, 214, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, | |
| 168363 | + /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1188, 474, 135, | |
| 168364 | + /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120, | |
| 168365 | + /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1555, | |
| 168366 | + /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1212, 1212, | |
| 168041 | 168367 | /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168042 | - /* 570 */ 1550, 122, 122, 122, 122, 121, 121, 120, 120, 120, | |
| 168043 | - /* 580 */ 119, 116, 444, 485, 1189, 1190, 1189, 482, 281, 1263, | |
| 168044 | - /* 590 */ 955, 252, 1189, 373, 504, 501, 500, 1189, 340, 570, | |
| 168045 | - /* 600 */ 1189, 570, 409, 292, 499, 955, 874, 191, 480, 316, | |
| 168368 | + /* 570 */ 1549, 122, 122, 122, 122, 121, 121, 120, 120, 120, | |
| 168369 | + /* 580 */ 119, 116, 444, 485, 1188, 1189, 1188, 482, 281, 1263, | |
| 168370 | + /* 590 */ 955, 252, 1188, 373, 504, 501, 500, 1188, 340, 570, | |
| 168371 | + /* 600 */ 1188, 570, 409, 292, 499, 955, 874, 191, 480, 316, | |
| 168046 | 168372 | /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, |
| 168047 | - /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, | |
| 168048 | - /* 630 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168049 | - /* 640 */ 124, 409, 394, 1133, 1189, 867, 100, 284, 284, 1189, | |
| 168050 | - /* 650 */ 1190, 1189, 373, 1090, 1189, 1190, 1189, 1189, 1190, 1189, | |
| 168051 | - /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1213, 1213, | |
| 168373 | + /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, | |
| 168374 | + /* 630 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168375 | + /* 640 */ 124, 409, 394, 1132, 1188, 867, 100, 284, 284, 1188, | |
| 168376 | + /* 650 */ 1189, 1188, 373, 1089, 1188, 1189, 1188, 1188, 1189, 1188, | |
| 168377 | + /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1212, 1212, | |
| 168052 | 168378 | /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168053 | 168379 | /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, |
| 168054 | - /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1155, 228, 1189, | |
| 168055 | - /* 700 */ 157, 1189, 1190, 1189, 1548, 13, 13, 301, 955, 1228, | |
| 168056 | - /* 710 */ 1155, 153, 409, 1155, 373, 1578, 1173, 5, 369, 1575, | |
| 168380 | + /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1154, 228, 1188, | |
| 168381 | + /* 700 */ 157, 1188, 1189, 1188, 1547, 13, 13, 301, 955, 1228, | |
| 168382 | + /* 710 */ 1154, 153, 409, 1154, 373, 1577, 1172, 5, 369, 1574, | |
| 168057 | 168383 | /* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121, |
| 168058 | - /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, | |
| 168059 | - /* 740 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168060 | - /* 750 */ 124, 409, 208, 567, 1189, 1025, 1189, 1190, 1189, 1189, | |
| 168061 | - /* 760 */ 388, 850, 155, 1547, 286, 402, 1095, 1095, 488, 568, | |
| 168062 | - /* 770 */ 465, 342, 1315, 1315, 1547, 125, 126, 80, 1213, 1213, | |
| 168384 | + /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, | |
| 168385 | + /* 740 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168386 | + /* 750 */ 124, 409, 208, 567, 1188, 1025, 1188, 1189, 1188, 1188, | |
| 168387 | + /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568, | |
| 168388 | + /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1212, 1212, | |
| 168063 | 168389 | /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168064 | 168390 | /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, |
| 168065 | 168391 | /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, |
| 168066 | - /* 810 */ 528, 1189, 1190, 1189, 13, 13, 1189, 1190, 1189, 1293, | |
| 168067 | - /* 820 */ 463, 1263, 409, 1313, 1313, 1547, 1010, 453, 452, 200, | |
| 168392 | + /* 810 */ 528, 1188, 1189, 1188, 13, 13, 1188, 1189, 1188, 1293, | |
| 168393 | + /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200, | |
| 168068 | 168394 | /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, |
| 168069 | - /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, | |
| 168070 | - /* 850 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168071 | - /* 860 */ 124, 409, 227, 1070, 1155, 284, 284, 419, 312, 278, | |
| 168072 | - /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1155, 565, 568, | |
| 168073 | - /* 880 */ 1155, 1192, 565, 1595, 565, 125, 126, 80, 1213, 1213, | |
| 168395 | + /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, | |
| 168396 | + /* 850 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168397 | + /* 860 */ 124, 409, 227, 1069, 1154, 284, 284, 419, 312, 278, | |
| 168398 | + /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1154, 565, 568, | |
| 168399 | + /* 880 */ 1154, 1191, 565, 1594, 565, 125, 126, 80, 1212, 1212, | |
| 168074 | 168400 | /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168075 | - /* 900 */ 453, 1477, 13, 13, 1531, 122, 122, 122, 122, 121, | |
| 168401 | + /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121, | |
| 168076 | 168402 | /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, |
| 168077 | - /* 920 */ 1581, 574, 2, 1241, 838, 839, 840, 1557, 317, 1208, | |
| 168078 | - /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1192, | |
| 168403 | + /* 920 */ 1580, 574, 2, 1241, 838, 839, 840, 1556, 317, 1207, | |
| 168404 | + /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1191, | |
| 168079 | 168405 | /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, |
| 168080 | - /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, | |
| 168081 | - /* 960 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168082 | - /* 970 */ 124, 568, 284, 284, 568, 1209, 409, 573, 313, 1241, | |
| 168406 | + /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, | |
| 168407 | + /* 960 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168408 | + /* 970 */ 124, 568, 284, 284, 568, 1208, 409, 573, 313, 1241, | |
| 168083 | 168409 | /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637, |
| 168084 | 168410 | /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, |
| 168085 | - /* 1000 */ 1321, 104, 80, 1213, 1213, 1047, 1050, 1037, 1037, 123, | |
| 168411 | + /* 1000 */ 1321, 104, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, | |
| 168086 | 168412 | /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, |
| 168087 | - /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1111, 284, 284, | |
| 168088 | - /* 1030 */ 428, 448, 1520, 1209, 439, 284, 284, 1484, 1348, 311, | |
| 168089 | - /* 1040 */ 474, 565, 1112, 969, 491, 491, 217, 1259, 565, 1533, | |
| 168090 | - /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1113, 519, 122, | |
| 168413 | + /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284, | |
| 168414 | + /* 1030 */ 428, 448, 1519, 1208, 439, 284, 284, 1483, 1348, 311, | |
| 168415 | + /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532, | |
| 168416 | + /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122, | |
| 168091 | 168417 | /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168092 | 168418 | /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568, |
| 168093 | - /* 1080 */ 1490, 568, 284, 284, 97, 526, 491, 448, 911, 1322, | |
| 168094 | - /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1490, 1492, | |
| 168419 | + /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322, | |
| 168420 | + /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491, | |
| 168095 | 168421 | /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, |
| 168096 | - /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1213, | |
| 168097 | - /* 1120 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168098 | - /* 1130 */ 124, 347, 409, 862, 1529, 1209, 125, 126, 80, 1213, | |
| 168099 | - /* 1140 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168100 | - /* 1150 */ 124, 1134, 1635, 474, 1635, 371, 125, 114, 80, 1213, | |
| 168101 | - /* 1160 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168102 | - /* 1170 */ 124, 1490, 329, 474, 331, 122, 122, 122, 122, 121, | |
| 168422 | + /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1212, | |
| 168423 | + /* 1120 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168424 | + /* 1130 */ 124, 347, 409, 862, 1528, 1208, 125, 126, 80, 1212, | |
| 168425 | + /* 1140 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168426 | + /* 1150 */ 124, 1133, 1635, 474, 1635, 371, 125, 114, 80, 1212, | |
| 168427 | + /* 1160 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, | |
| 168428 | + /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121, | |
| 168103 | 168429 | /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, |
| 168104 | - /* 1190 */ 1290, 862, 464, 1209, 436, 122, 122, 122, 122, 121, | |
| 168105 | - /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1134, 1636, | |
| 168430 | + /* 1190 */ 1290, 862, 464, 1208, 436, 122, 122, 122, 122, 121, | |
| 168431 | + /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1636, | |
| 168106 | 168432 | /* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121, |
| 168107 | 168433 | /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, |
| 168108 | - /* 1230 */ 1132, 1415, 1554, 1555, 1327, 409, 6, 6, 1166, 1264, | |
| 168434 | + /* 1230 */ 1131, 1415, 1553, 1554, 1327, 409, 6, 6, 1165, 1264, | |
| 168109 | 168435 | /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, |
| 168110 | 168436 | /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, |
| 168111 | - /* 1260 */ 126, 80, 1213, 1213, 1047, 1050, 1037, 1037, 123, 123, | |
| 168112 | - /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1189, 1415, | |
| 168113 | - /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1132, 1553, 847, | |
| 168114 | - /* 1290 */ 1166, 407, 6, 568, 321, 1155, 470, 44, 44, 1552, | |
| 168115 | - /* 1300 */ 1111, 426, 234, 6, 323, 256, 540, 256, 1155, 431, | |
| 168116 | - /* 1310 */ 568, 1155, 322, 17, 487, 1112, 58, 58, 122, 122, | |
| 168437 | + /* 1260 */ 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, | |
| 168438 | + /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1188, 1415, | |
| 168439 | + /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1552, 847, | |
| 168440 | + /* 1290 */ 1165, 407, 6, 568, 321, 1154, 470, 44, 44, 1551, | |
| 168441 | + /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1154, 431, | |
| 168442 | + /* 1310 */ 568, 1154, 322, 17, 487, 1111, 58, 58, 122, 122, | |
| 168117 | 168443 | /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, |
| 168118 | - /* 1330 */ 1113, 216, 481, 59, 59, 1189, 1190, 1189, 111, 560, | |
| 168444 | + /* 1330 */ 1112, 216, 481, 59, 59, 1188, 1189, 1188, 111, 560, | |
| 168119 | 168445 | /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, |
| 168120 | - /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1092, | |
| 168121 | - /* 1360 */ 568, 293, 568, 1092, 531, 568, 870, 8, 60, 60, | |
| 168446 | + /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091, | |
| 168447 | + /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60, | |
| 168122 | 168448 | /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, |
| 168123 | 168449 | /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, |
| 168124 | 168450 | /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, |
| 168125 | 168451 | /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534, |
| 168126 | 168452 | /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024, |
| @@ -168127,73 +168453,73 @@ | ||
| 168127 | 168453 | /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66, |
| 168128 | 168454 | /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177, |
| 168129 | 168455 | /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, |
| 168130 | 168456 | /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, |
| 168131 | 168457 | /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, |
| 168132 | - /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1580, 1177, 447, | |
| 168133 | - /* 1480 */ 69, 69, 288, 97, 108, 1536, 106, 392, 392, 391, | |
| 168458 | + /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1579, 1176, 447, | |
| 168459 | + /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391, | |
| 168134 | 168460 | /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, |
| 168135 | - /* 1500 */ 4, 568, 152, 30, 38, 568, 1129, 234, 396, 323, | |
| 168461 | + /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323, | |
| 168136 | 168462 | /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, |
| 168137 | 168463 | /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, |
| 168138 | - /* 1530 */ 568, 289, 1509, 568, 31, 1508, 568, 445, 338, 483, | |
| 168139 | - /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1077, 557, | |
| 168464 | + /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483, | |
| 168465 | + /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557, | |
| 168140 | 168466 | /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, |
| 168141 | - /* 1560 */ 161, 1569, 557, 535, 568, 319, 568, 348, 536, 1007, | |
| 168467 | + /* 1560 */ 161, 1568, 557, 535, 568, 319, 568, 348, 536, 1007, | |
| 168142 | 168468 | /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, |
| 168143 | 168469 | /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, |
| 168144 | 168470 | /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, |
| 168145 | - /* 1600 */ 162, 162, 156, 156, 568, 110, 1077, 445, 569, 445, | |
| 168471 | + /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445, | |
| 168146 | 168472 | /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, |
| 168147 | 168473 | /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, |
| 168148 | 168474 | /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, |
| 168149 | - /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1177, | |
| 168475 | + /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1176, | |
| 168150 | 168476 | /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, |
| 168151 | - /* 1660 */ 391, 273, 389, 568, 1138, 847, 568, 1073, 568, 258, | |
| 168477 | + /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258, | |
| 168152 | 168478 | /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, |
| 168153 | 168479 | /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74, |
| 168154 | 168480 | /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, |
| 168155 | - /* 1700 */ 973, 1089, 1088, 1089, 1088, 860, 557, 150, 928, 1342, | |
| 168481 | + /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342, | |
| 168156 | 168482 | /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, |
| 168157 | - /* 1720 */ 1251, 445, 1588, 1339, 308, 276, 168, 309, 11, 141, | |
| 168483 | + /* 1720 */ 1251, 445, 1587, 1339, 308, 276, 168, 309, 11, 141, | |
| 168158 | 168484 | /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, |
| 168159 | 168485 | /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, |
| 168160 | 168486 | /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, |
| 168161 | - /* 1760 */ 223, 1481, 1024, 1480, 1351, 1352, 1350, 1349, 109, 109, | |
| 168162 | - /* 1770 */ 204, 1591, 1228, 558, 265, 218, 110, 205, 445, 569, | |
| 168163 | - /* 1780 */ 445, 410, 387, 1014, 1528, 179, 316, 559, 1014, 1014, | |
| 168164 | - /* 1790 */ 1016, 1017, 27, 230, 1526, 1225, 79, 560, 85, 4, | |
| 168487 | + /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109, | |
| 168488 | + /* 1770 */ 204, 1590, 1228, 558, 265, 218, 110, 205, 445, 569, | |
| 168489 | + /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014, | |
| 168490 | + /* 1790 */ 1016, 1017, 27, 230, 1525, 1225, 79, 560, 85, 4, | |
| 168165 | 168491 | /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, |
| 168166 | 168492 | /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, |
| 168167 | - /* 1820 */ 184, 1486, 185, 186, 495, 242, 98, 398, 1408, 36, | |
| 168168 | - /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1475, 246, | |
| 168169 | - /* 1840 */ 1497, 490, 346, 277, 248, 196, 493, 511, 557, 350, | |
| 168493 | + /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36, | |
| 168494 | + /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246, | |
| 168495 | + /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350, | |
| 168170 | 168496 | /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, |
| 168171 | - /* 1860 */ 1307, 1300, 93, 1605, 881, 1604, 224, 404, 434, 520, | |
| 168172 | - /* 1870 */ 263, 435, 1574, 563, 1279, 1278, 364, 1024, 306, 1277, | |
| 168173 | - /* 1880 */ 264, 1603, 1560, 109, 109, 370, 1299, 307, 1559, 438, | |
| 168497 | + /* 1860 */ 1307, 1300, 93, 1604, 881, 1603, 224, 404, 434, 520, | |
| 168498 | + /* 1870 */ 263, 435, 1573, 563, 1279, 1278, 364, 1024, 306, 1277, | |
| 168499 | + /* 1880 */ 264, 1602, 1559, 109, 109, 370, 1299, 307, 1558, 438, | |
| 168174 | 168500 | /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, |
| 168175 | 168501 | /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, |
| 168176 | - /* 1910 */ 1183, 530, 272, 274, 379, 210, 1331, 547, 385, 386, | |
| 168177 | - /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1513, 165, 178, 1514, | |
| 168178 | - /* 1930 */ 1014, 1014, 1016, 1017, 27, 1512, 1511, 1024, 78, 147, | |
| 168502 | + /* 1910 */ 1182, 530, 272, 274, 379, 210, 1331, 547, 385, 386, | |
| 168503 | + /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513, | |
| 168504 | + /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147, | |
| 168179 | 168505 | /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, |
| 168180 | - /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1087, 1014, 1085, | |
| 168181 | - /* 1960 */ 326, 180, 169, 1208, 182, 334, 238, 913, 241, 1101, | |
| 168506 | + /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084, | |
| 168507 | + /* 1960 */ 326, 180, 169, 1207, 182, 334, 238, 913, 241, 1100, | |
| 168182 | 168508 | /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, |
| 168183 | - /* 1980 */ 172, 1104, 243, 1100, 244, 158, 18, 245, 345, 247, | |
| 168184 | - /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1093, 193, 1222, 489, | |
| 168509 | + /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247, | |
| 168510 | + /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1222, 489, | |
| 168185 | 168511 | /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, |
| 168186 | 168512 | /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, |
| 168187 | - /* 2020 */ 513, 39, 95, 1171, 160, 1053, 964, 1140, 96, 174, | |
| 168188 | - /* 2030 */ 1139, 225, 280, 282, 198, 958, 113, 1161, 1157, 260, | |
| 168189 | - /* 2040 */ 21, 22, 23, 1159, 1165, 1164, 1145, 24, 33, 25, | |
| 168190 | - /* 2050 */ 202, 542, 26, 100, 1068, 102, 1054, 103, 7, 1052, | |
| 168191 | - /* 2060 */ 1056, 1110, 1057, 1109, 266, 267, 28, 40, 390, 1019, | |
| 168192 | - /* 2070 */ 861, 112, 29, 564, 1179, 1178, 268, 176, 143, 923, | |
| 168513 | + /* 2020 */ 513, 39, 95, 1170, 160, 1053, 964, 1139, 96, 174, | |
| 168514 | + /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1160, 1156, 260, | |
| 168515 | + /* 2040 */ 21, 22, 23, 1158, 1164, 1163, 1144, 24, 33, 25, | |
| 168516 | + /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052, | |
| 168517 | + /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019, | |
| 168518 | + /* 2070 */ 861, 112, 29, 564, 1178, 1177, 268, 176, 143, 923, | |
| 168193 | 168519 | /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, |
| 168194 | - /* 2090 */ 1238, 1238, 1238, 1238, 269, 1596, | |
| 168520 | + /* 2090 */ 1238, 1238, 1238, 1238, 269, 1595, | |
| 168195 | 168521 | }; |
| 168196 | 168522 | static const YYCODETYPE yy_lookahead[] = { |
| 168197 | 168523 | /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, |
| 168198 | 168524 | /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, |
| 168199 | 168525 | /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, |
| @@ -168532,67 +168858,67 @@ | ||
| 168532 | 168858 | /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718, |
| 168533 | 168859 | /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716, |
| 168534 | 168860 | /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, |
| 168535 | 168861 | }; |
| 168536 | 168862 | static const YYACTIONTYPE yy_default[] = { |
| 168537 | - /* 0 */ 1641, 1641, 1641, 1470, 1236, 1347, 1236, 1236, 1236, 1470, | |
| 168538 | - /* 10 */ 1470, 1470, 1236, 1377, 1377, 1523, 1269, 1236, 1236, 1236, | |
| 168539 | - /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1469, 1236, 1236, | |
| 168540 | - /* 30 */ 1236, 1236, 1558, 1558, 1236, 1236, 1236, 1236, 1236, 1236, | |
| 168863 | + /* 0 */ 1641, 1641, 1641, 1469, 1236, 1347, 1236, 1236, 1236, 1469, | |
| 168864 | + /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236, | |
| 168865 | + /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236, | |
| 168866 | + /* 30 */ 1236, 1236, 1557, 1557, 1236, 1236, 1236, 1236, 1236, 1236, | |
| 168541 | 168867 | /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, |
| 168542 | - /* 50 */ 1471, 1472, 1236, 1236, 1236, 1522, 1524, 1487, 1400, 1399, | |
| 168543 | - /* 60 */ 1398, 1397, 1505, 1365, 1391, 1384, 1388, 1465, 1466, 1464, | |
| 168544 | - /* 70 */ 1468, 1472, 1471, 1236, 1387, 1433, 1449, 1432, 1236, 1236, | |
| 168868 | + /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399, | |
| 168869 | + /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464, | |
| 168870 | + /* 70 */ 1619, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, | |
| 168545 | 168871 | /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168546 | 168872 | /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168547 | 168873 | /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168548 | 168874 | /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168549 | 168875 | /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168550 | 168876 | /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, |
| 168551 | 168877 | /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, |
| 168552 | - /* 150 */ 1236, 1236, 1236, 1542, 1541, 1236, 1438, 1236, 1269, 1427, | |
| 168553 | - /* 160 */ 1426, 1452, 1439, 1451, 1450, 1530, 1594, 1593, 1488, 1236, | |
| 168554 | - /* 170 */ 1236, 1236, 1236, 1236, 1236, 1558, 1236, 1236, 1236, 1236, | |
| 168878 | + /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427, | |
| 168879 | + /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1593, 1592, 1487, 1236, | |
| 168880 | + /* 170 */ 1236, 1236, 1236, 1236, 1236, 1557, 1236, 1236, 1236, 1236, | |
| 168555 | 168881 | /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168556 | 168882 | /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, |
| 168557 | - /* 200 */ 1558, 1558, 1236, 1269, 1558, 1558, 1368, 1368, 1265, 1265, | |
| 168558 | - /* 210 */ 1371, 1236, 1537, 1338, 1338, 1338, 1338, 1347, 1338, 1236, | |
| 168883 | + /* 200 */ 1557, 1557, 1236, 1269, 1557, 1557, 1368, 1368, 1265, 1265, | |
| 168884 | + /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236, | |
| 168559 | 168885 | /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168560 | - /* 230 */ 1236, 1236, 1236, 1236, 1527, 1525, 1236, 1236, 1236, 1236, | |
| 168886 | + /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236, | |
| 168561 | 168887 | /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168562 | 168888 | /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168563 | 168889 | /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168564 | - /* 270 */ 1236, 1236, 1236, 1236, 1236, 1587, 1236, 1500, 1325, 1343, | |
| 168890 | + /* 270 */ 1236, 1236, 1236, 1236, 1236, 1586, 1236, 1499, 1325, 1343, | |
| 168565 | 168891 | /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633, |
| 168566 | 168892 | /* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403, |
| 168567 | - /* 300 */ 1344, 1630, 1286, 1609, 1281, 1377, 1377, 1377, 1367, 1367, | |
| 168893 | + /* 300 */ 1344, 1630, 1286, 1608, 1281, 1377, 1377, 1377, 1367, 1367, | |
| 168568 | 168894 | /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633, |
| 168569 | - /* 320 */ 1353, 1353, 1632, 1632, 1353, 1488, 1617, 1412, 1314, 1320, | |
| 168570 | - /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1617, 1617, 1390, 1412, | |
| 168571 | - /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1504, 1627, 1353, 1254, | |
| 168572 | - /* 350 */ 1478, 1353, 1254, 1353, 1254, 1478, 1312, 1312, 1312, 1301, | |
| 168573 | - /* 360 */ 1236, 1236, 1478, 1312, 1286, 1312, 1301, 1312, 1312, 1576, | |
| 168574 | - /* 370 */ 1236, 1482, 1482, 1478, 1353, 1568, 1568, 1380, 1380, 1385, | |
| 168575 | - /* 380 */ 1371, 1473, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1590, | |
| 168576 | - /* 390 */ 1590, 1586, 1586, 1586, 1638, 1638, 1537, 1602, 1269, 1269, | |
| 168577 | - /* 400 */ 1269, 1269, 1602, 1288, 1288, 1270, 1270, 1269, 1602, 1236, | |
| 168578 | - /* 410 */ 1236, 1236, 1236, 1236, 1236, 1597, 1236, 1532, 1489, 1357, | |
| 168895 | + /* 320 */ 1353, 1353, 1632, 1632, 1353, 1487, 1616, 1412, 1314, 1320, | |
| 168896 | + /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1616, 1616, 1390, 1412, | |
| 168897 | + /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1627, 1353, 1254, | |
| 168898 | + /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301, | |
| 168899 | + /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1575, | |
| 168900 | + /* 370 */ 1236, 1481, 1481, 1477, 1353, 1567, 1567, 1380, 1380, 1385, | |
| 168901 | + /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1589, | |
| 168902 | + /* 390 */ 1589, 1585, 1585, 1585, 1638, 1638, 1536, 1601, 1269, 1269, | |
| 168903 | + /* 400 */ 1269, 1269, 1601, 1288, 1288, 1270, 1270, 1269, 1601, 1236, | |
| 168904 | + /* 410 */ 1236, 1236, 1236, 1236, 1236, 1596, 1236, 1531, 1488, 1357, | |
| 168579 | 168905 | /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168580 | - /* 430 */ 1236, 1236, 1236, 1236, 1543, 1236, 1236, 1236, 1236, 1236, | |
| 168581 | - /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1534, 1236, | |
| 168906 | + /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236, | |
| 168907 | + /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236, | |
| 168582 | 168908 | /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, |
| 168583 | 168909 | /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, |
| 168584 | 168910 | /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168585 | - /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1503, 1502, 1236, | |
| 168911 | + /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, | |
| 168586 | 168912 | /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168587 | 168913 | /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, |
| 168588 | 168914 | /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168589 | 168915 | /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, |
| 168590 | 168916 | /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168591 | - /* 540 */ 1236, 1236, 1236, 1236, 1573, 1372, 1236, 1236, 1236, 1236, | |
| 168917 | + /* 540 */ 1236, 1236, 1236, 1236, 1572, 1372, 1236, 1236, 1236, 1236, | |
| 168592 | 168918 | /* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168593 | - /* 560 */ 1236, 1236, 1236, 1236, 1236, 1613, 1328, 1418, 1236, 1421, | |
| 168919 | + /* 560 */ 1236, 1236, 1236, 1236, 1236, 1612, 1328, 1418, 1236, 1421, | |
| 168594 | 168920 | /* 570 */ 1258, 1236, 1248, 1236, 1236, |
| 168595 | 168921 | }; |
| 168596 | 168922 | /********** End of lemon-generated parsing tables *****************************/ |
| 168597 | 168923 | |
| 168598 | 168924 | /* The next table maps tokens (terminal symbols) into fallback tokens. |
| @@ -169437,162 +169763,162 @@ | ||
| 169437 | 169763 | /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", |
| 169438 | 169764 | /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", |
| 169439 | 169765 | /* 226 */ "case_exprlist ::= WHEN expr THEN expr", |
| 169440 | 169766 | /* 227 */ "case_else ::= ELSE expr", |
| 169441 | 169767 | /* 228 */ "case_else ::=", |
| 169442 | - /* 229 */ "case_operand ::= expr", | |
| 169443 | - /* 230 */ "case_operand ::=", | |
| 169444 | - /* 231 */ "exprlist ::=", | |
| 169445 | - /* 232 */ "nexprlist ::= nexprlist COMMA expr", | |
| 169446 | - /* 233 */ "nexprlist ::= expr", | |
| 169447 | - /* 234 */ "paren_exprlist ::=", | |
| 169448 | - /* 235 */ "paren_exprlist ::= LP exprlist RP", | |
| 169449 | - /* 236 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", | |
| 169450 | - /* 237 */ "uniqueflag ::= UNIQUE", | |
| 169451 | - /* 238 */ "uniqueflag ::=", | |
| 169452 | - /* 239 */ "eidlist_opt ::=", | |
| 169453 | - /* 240 */ "eidlist_opt ::= LP eidlist RP", | |
| 169454 | - /* 241 */ "eidlist ::= eidlist COMMA nm collate sortorder", | |
| 169455 | - /* 242 */ "eidlist ::= nm collate sortorder", | |
| 169456 | - /* 243 */ "collate ::=", | |
| 169457 | - /* 244 */ "collate ::= COLLATE ID|STRING", | |
| 169458 | - /* 245 */ "cmd ::= DROP INDEX ifexists fullname", | |
| 169459 | - /* 246 */ "cmd ::= VACUUM vinto", | |
| 169460 | - /* 247 */ "cmd ::= VACUUM nm vinto", | |
| 169461 | - /* 248 */ "vinto ::= INTO expr", | |
| 169462 | - /* 249 */ "vinto ::=", | |
| 169463 | - /* 250 */ "cmd ::= PRAGMA nm dbnm", | |
| 169464 | - /* 251 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", | |
| 169465 | - /* 252 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", | |
| 169466 | - /* 253 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", | |
| 169467 | - /* 254 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", | |
| 169468 | - /* 255 */ "plus_num ::= PLUS INTEGER|FLOAT", | |
| 169469 | - /* 256 */ "minus_num ::= MINUS INTEGER|FLOAT", | |
| 169470 | - /* 257 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", | |
| 169471 | - /* 258 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", | |
| 169472 | - /* 259 */ "trigger_time ::= BEFORE|AFTER", | |
| 169473 | - /* 260 */ "trigger_time ::= INSTEAD OF", | |
| 169474 | - /* 261 */ "trigger_time ::=", | |
| 169475 | - /* 262 */ "trigger_event ::= DELETE|INSERT", | |
| 169476 | - /* 263 */ "trigger_event ::= UPDATE", | |
| 169477 | - /* 264 */ "trigger_event ::= UPDATE OF idlist", | |
| 169478 | - /* 265 */ "when_clause ::=", | |
| 169479 | - /* 266 */ "when_clause ::= WHEN expr", | |
| 169480 | - /* 267 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", | |
| 169481 | - /* 268 */ "trigger_cmd_list ::= trigger_cmd SEMI", | |
| 169482 | - /* 269 */ "trnm ::= nm DOT nm", | |
| 169483 | - /* 270 */ "tridxby ::= INDEXED BY nm", | |
| 169484 | - /* 271 */ "tridxby ::= NOT INDEXED", | |
| 169485 | - /* 272 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", | |
| 169486 | - /* 273 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", | |
| 169487 | - /* 274 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", | |
| 169488 | - /* 275 */ "trigger_cmd ::= scanpt select scanpt", | |
| 169489 | - /* 276 */ "expr ::= RAISE LP IGNORE RP", | |
| 169490 | - /* 277 */ "expr ::= RAISE LP raisetype COMMA nm RP", | |
| 169491 | - /* 278 */ "raisetype ::= ROLLBACK", | |
| 169492 | - /* 279 */ "raisetype ::= ABORT", | |
| 169493 | - /* 280 */ "raisetype ::= FAIL", | |
| 169494 | - /* 281 */ "cmd ::= DROP TRIGGER ifexists fullname", | |
| 169495 | - /* 282 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", | |
| 169496 | - /* 283 */ "cmd ::= DETACH database_kw_opt expr", | |
| 169497 | - /* 284 */ "key_opt ::=", | |
| 169498 | - /* 285 */ "key_opt ::= KEY expr", | |
| 169499 | - /* 286 */ "cmd ::= REINDEX", | |
| 169500 | - /* 287 */ "cmd ::= REINDEX nm dbnm", | |
| 169501 | - /* 288 */ "cmd ::= ANALYZE", | |
| 169502 | - /* 289 */ "cmd ::= ANALYZE nm dbnm", | |
| 169503 | - /* 290 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", | |
| 169504 | - /* 291 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", | |
| 169505 | - /* 292 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", | |
| 169506 | - /* 293 */ "add_column_fullname ::= fullname", | |
| 169507 | - /* 294 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", | |
| 169508 | - /* 295 */ "cmd ::= create_vtab", | |
| 169509 | - /* 296 */ "cmd ::= create_vtab LP vtabarglist RP", | |
| 169510 | - /* 297 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", | |
| 169511 | - /* 298 */ "vtabarg ::=", | |
| 169512 | - /* 299 */ "vtabargtoken ::= ANY", | |
| 169513 | - /* 300 */ "vtabargtoken ::= lp anylist RP", | |
| 169514 | - /* 301 */ "lp ::= LP", | |
| 169515 | - /* 302 */ "with ::= WITH wqlist", | |
| 169516 | - /* 303 */ "with ::= WITH RECURSIVE wqlist", | |
| 169517 | - /* 304 */ "wqas ::= AS", | |
| 169518 | - /* 305 */ "wqas ::= AS MATERIALIZED", | |
| 169519 | - /* 306 */ "wqas ::= AS NOT MATERIALIZED", | |
| 169520 | - /* 307 */ "wqitem ::= nm eidlist_opt wqas LP select RP", | |
| 169521 | - /* 308 */ "wqlist ::= wqitem", | |
| 169522 | - /* 309 */ "wqlist ::= wqlist COMMA wqitem", | |
| 169523 | - /* 310 */ "windowdefn_list ::= windowdefn", | |
| 169524 | - /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", | |
| 169525 | - /* 312 */ "windowdefn ::= nm AS LP window RP", | |
| 169526 | - /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", | |
| 169527 | - /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", | |
| 169528 | - /* 315 */ "window ::= ORDER BY sortlist frame_opt", | |
| 169529 | - /* 316 */ "window ::= nm ORDER BY sortlist frame_opt", | |
| 169530 | - /* 317 */ "window ::= frame_opt", | |
| 169531 | - /* 318 */ "window ::= nm frame_opt", | |
| 169532 | - /* 319 */ "frame_opt ::=", | |
| 169533 | - /* 320 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", | |
| 169534 | - /* 321 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", | |
| 169535 | - /* 322 */ "range_or_rows ::= RANGE|ROWS|GROUPS", | |
| 169536 | - /* 323 */ "frame_bound_s ::= frame_bound", | |
| 169537 | - /* 324 */ "frame_bound_s ::= UNBOUNDED PRECEDING", | |
| 169538 | - /* 325 */ "frame_bound_e ::= frame_bound", | |
| 169539 | - /* 326 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", | |
| 169540 | - /* 327 */ "frame_bound ::= expr PRECEDING|FOLLOWING", | |
| 169541 | - /* 328 */ "frame_bound ::= CURRENT ROW", | |
| 169542 | - /* 329 */ "frame_exclude_opt ::=", | |
| 169543 | - /* 330 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", | |
| 169544 | - /* 331 */ "frame_exclude ::= NO OTHERS", | |
| 169545 | - /* 332 */ "frame_exclude ::= CURRENT ROW", | |
| 169546 | - /* 333 */ "frame_exclude ::= GROUP|TIES", | |
| 169547 | - /* 334 */ "window_clause ::= WINDOW windowdefn_list", | |
| 169548 | - /* 335 */ "filter_over ::= filter_clause over_clause", | |
| 169549 | - /* 336 */ "filter_over ::= over_clause", | |
| 169550 | - /* 337 */ "filter_over ::= filter_clause", | |
| 169551 | - /* 338 */ "over_clause ::= OVER LP window RP", | |
| 169552 | - /* 339 */ "over_clause ::= OVER nm", | |
| 169553 | - /* 340 */ "filter_clause ::= FILTER LP WHERE expr RP", | |
| 169554 | - /* 341 */ "input ::= cmdlist", | |
| 169555 | - /* 342 */ "cmdlist ::= cmdlist ecmd", | |
| 169556 | - /* 343 */ "cmdlist ::= ecmd", | |
| 169557 | - /* 344 */ "ecmd ::= SEMI", | |
| 169558 | - /* 345 */ "ecmd ::= cmdx SEMI", | |
| 169559 | - /* 346 */ "ecmd ::= explain cmdx SEMI", | |
| 169560 | - /* 347 */ "trans_opt ::=", | |
| 169561 | - /* 348 */ "trans_opt ::= TRANSACTION", | |
| 169562 | - /* 349 */ "trans_opt ::= TRANSACTION nm", | |
| 169563 | - /* 350 */ "savepoint_opt ::= SAVEPOINT", | |
| 169564 | - /* 351 */ "savepoint_opt ::=", | |
| 169565 | - /* 352 */ "cmd ::= create_table create_table_args", | |
| 169566 | - /* 353 */ "table_option_set ::= table_option", | |
| 169567 | - /* 354 */ "columnlist ::= columnlist COMMA columnname carglist", | |
| 169568 | - /* 355 */ "columnlist ::= columnname carglist", | |
| 169569 | - /* 356 */ "nm ::= ID|INDEXED|JOIN_KW", | |
| 169570 | - /* 357 */ "nm ::= STRING", | |
| 169571 | - /* 358 */ "typetoken ::= typename", | |
| 169572 | - /* 359 */ "typename ::= ID|STRING", | |
| 169573 | - /* 360 */ "signed ::= plus_num", | |
| 169574 | - /* 361 */ "signed ::= minus_num", | |
| 169575 | - /* 362 */ "carglist ::= carglist ccons", | |
| 169576 | - /* 363 */ "carglist ::=", | |
| 169577 | - /* 364 */ "ccons ::= NULL onconf", | |
| 169578 | - /* 365 */ "ccons ::= GENERATED ALWAYS AS generated", | |
| 169579 | - /* 366 */ "ccons ::= AS generated", | |
| 169580 | - /* 367 */ "conslist_opt ::= COMMA conslist", | |
| 169581 | - /* 368 */ "conslist ::= conslist tconscomma tcons", | |
| 169582 | - /* 369 */ "conslist ::= tcons", | |
| 169583 | - /* 370 */ "tconscomma ::=", | |
| 169584 | - /* 371 */ "defer_subclause_opt ::= defer_subclause", | |
| 169585 | - /* 372 */ "resolvetype ::= raisetype", | |
| 169586 | - /* 373 */ "selectnowith ::= oneselect", | |
| 169587 | - /* 374 */ "oneselect ::= values", | |
| 169588 | - /* 375 */ "sclp ::= selcollist COMMA", | |
| 169589 | - /* 376 */ "as ::= ID|STRING", | |
| 169590 | - /* 377 */ "indexed_opt ::= indexed_by", | |
| 169591 | - /* 378 */ "returning ::=", | |
| 169592 | - /* 379 */ "expr ::= term", | |
| 169593 | - /* 380 */ "likeop ::= LIKE_KW|MATCH", | |
| 169768 | + /* 229 */ "case_operand ::=", | |
| 169769 | + /* 230 */ "exprlist ::=", | |
| 169770 | + /* 231 */ "nexprlist ::= nexprlist COMMA expr", | |
| 169771 | + /* 232 */ "nexprlist ::= expr", | |
| 169772 | + /* 233 */ "paren_exprlist ::=", | |
| 169773 | + /* 234 */ "paren_exprlist ::= LP exprlist RP", | |
| 169774 | + /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", | |
| 169775 | + /* 236 */ "uniqueflag ::= UNIQUE", | |
| 169776 | + /* 237 */ "uniqueflag ::=", | |
| 169777 | + /* 238 */ "eidlist_opt ::=", | |
| 169778 | + /* 239 */ "eidlist_opt ::= LP eidlist RP", | |
| 169779 | + /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", | |
| 169780 | + /* 241 */ "eidlist ::= nm collate sortorder", | |
| 169781 | + /* 242 */ "collate ::=", | |
| 169782 | + /* 243 */ "collate ::= COLLATE ID|STRING", | |
| 169783 | + /* 244 */ "cmd ::= DROP INDEX ifexists fullname", | |
| 169784 | + /* 245 */ "cmd ::= VACUUM vinto", | |
| 169785 | + /* 246 */ "cmd ::= VACUUM nm vinto", | |
| 169786 | + /* 247 */ "vinto ::= INTO expr", | |
| 169787 | + /* 248 */ "vinto ::=", | |
| 169788 | + /* 249 */ "cmd ::= PRAGMA nm dbnm", | |
| 169789 | + /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", | |
| 169790 | + /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", | |
| 169791 | + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", | |
| 169792 | + /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", | |
| 169793 | + /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", | |
| 169794 | + /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", | |
| 169795 | + /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", | |
| 169796 | + /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", | |
| 169797 | + /* 258 */ "trigger_time ::= BEFORE|AFTER", | |
| 169798 | + /* 259 */ "trigger_time ::= INSTEAD OF", | |
| 169799 | + /* 260 */ "trigger_time ::=", | |
| 169800 | + /* 261 */ "trigger_event ::= DELETE|INSERT", | |
| 169801 | + /* 262 */ "trigger_event ::= UPDATE", | |
| 169802 | + /* 263 */ "trigger_event ::= UPDATE OF idlist", | |
| 169803 | + /* 264 */ "when_clause ::=", | |
| 169804 | + /* 265 */ "when_clause ::= WHEN expr", | |
| 169805 | + /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", | |
| 169806 | + /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", | |
| 169807 | + /* 268 */ "trnm ::= nm DOT nm", | |
| 169808 | + /* 269 */ "tridxby ::= INDEXED BY nm", | |
| 169809 | + /* 270 */ "tridxby ::= NOT INDEXED", | |
| 169810 | + /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", | |
| 169811 | + /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", | |
| 169812 | + /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", | |
| 169813 | + /* 274 */ "trigger_cmd ::= scanpt select scanpt", | |
| 169814 | + /* 275 */ "expr ::= RAISE LP IGNORE RP", | |
| 169815 | + /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", | |
| 169816 | + /* 277 */ "raisetype ::= ROLLBACK", | |
| 169817 | + /* 278 */ "raisetype ::= ABORT", | |
| 169818 | + /* 279 */ "raisetype ::= FAIL", | |
| 169819 | + /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", | |
| 169820 | + /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", | |
| 169821 | + /* 282 */ "cmd ::= DETACH database_kw_opt expr", | |
| 169822 | + /* 283 */ "key_opt ::=", | |
| 169823 | + /* 284 */ "key_opt ::= KEY expr", | |
| 169824 | + /* 285 */ "cmd ::= REINDEX", | |
| 169825 | + /* 286 */ "cmd ::= REINDEX nm dbnm", | |
| 169826 | + /* 287 */ "cmd ::= ANALYZE", | |
| 169827 | + /* 288 */ "cmd ::= ANALYZE nm dbnm", | |
| 169828 | + /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", | |
| 169829 | + /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", | |
| 169830 | + /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", | |
| 169831 | + /* 292 */ "add_column_fullname ::= fullname", | |
| 169832 | + /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", | |
| 169833 | + /* 294 */ "cmd ::= create_vtab", | |
| 169834 | + /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", | |
| 169835 | + /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", | |
| 169836 | + /* 297 */ "vtabarg ::=", | |
| 169837 | + /* 298 */ "vtabargtoken ::= ANY", | |
| 169838 | + /* 299 */ "vtabargtoken ::= lp anylist RP", | |
| 169839 | + /* 300 */ "lp ::= LP", | |
| 169840 | + /* 301 */ "with ::= WITH wqlist", | |
| 169841 | + /* 302 */ "with ::= WITH RECURSIVE wqlist", | |
| 169842 | + /* 303 */ "wqas ::= AS", | |
| 169843 | + /* 304 */ "wqas ::= AS MATERIALIZED", | |
| 169844 | + /* 305 */ "wqas ::= AS NOT MATERIALIZED", | |
| 169845 | + /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", | |
| 169846 | + /* 307 */ "wqlist ::= wqitem", | |
| 169847 | + /* 308 */ "wqlist ::= wqlist COMMA wqitem", | |
| 169848 | + /* 309 */ "windowdefn_list ::= windowdefn", | |
| 169849 | + /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", | |
| 169850 | + /* 311 */ "windowdefn ::= nm AS LP window RP", | |
| 169851 | + /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", | |
| 169852 | + /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", | |
| 169853 | + /* 314 */ "window ::= ORDER BY sortlist frame_opt", | |
| 169854 | + /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", | |
| 169855 | + /* 316 */ "window ::= frame_opt", | |
| 169856 | + /* 317 */ "window ::= nm frame_opt", | |
| 169857 | + /* 318 */ "frame_opt ::=", | |
| 169858 | + /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", | |
| 169859 | + /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", | |
| 169860 | + /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", | |
| 169861 | + /* 322 */ "frame_bound_s ::= frame_bound", | |
| 169862 | + /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", | |
| 169863 | + /* 324 */ "frame_bound_e ::= frame_bound", | |
| 169864 | + /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", | |
| 169865 | + /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", | |
| 169866 | + /* 327 */ "frame_bound ::= CURRENT ROW", | |
| 169867 | + /* 328 */ "frame_exclude_opt ::=", | |
| 169868 | + /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", | |
| 169869 | + /* 330 */ "frame_exclude ::= NO OTHERS", | |
| 169870 | + /* 331 */ "frame_exclude ::= CURRENT ROW", | |
| 169871 | + /* 332 */ "frame_exclude ::= GROUP|TIES", | |
| 169872 | + /* 333 */ "window_clause ::= WINDOW windowdefn_list", | |
| 169873 | + /* 334 */ "filter_over ::= filter_clause over_clause", | |
| 169874 | + /* 335 */ "filter_over ::= over_clause", | |
| 169875 | + /* 336 */ "filter_over ::= filter_clause", | |
| 169876 | + /* 337 */ "over_clause ::= OVER LP window RP", | |
| 169877 | + /* 338 */ "over_clause ::= OVER nm", | |
| 169878 | + /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", | |
| 169879 | + /* 340 */ "input ::= cmdlist", | |
| 169880 | + /* 341 */ "cmdlist ::= cmdlist ecmd", | |
| 169881 | + /* 342 */ "cmdlist ::= ecmd", | |
| 169882 | + /* 343 */ "ecmd ::= SEMI", | |
| 169883 | + /* 344 */ "ecmd ::= cmdx SEMI", | |
| 169884 | + /* 345 */ "ecmd ::= explain cmdx SEMI", | |
| 169885 | + /* 346 */ "trans_opt ::=", | |
| 169886 | + /* 347 */ "trans_opt ::= TRANSACTION", | |
| 169887 | + /* 348 */ "trans_opt ::= TRANSACTION nm", | |
| 169888 | + /* 349 */ "savepoint_opt ::= SAVEPOINT", | |
| 169889 | + /* 350 */ "savepoint_opt ::=", | |
| 169890 | + /* 351 */ "cmd ::= create_table create_table_args", | |
| 169891 | + /* 352 */ "table_option_set ::= table_option", | |
| 169892 | + /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", | |
| 169893 | + /* 354 */ "columnlist ::= columnname carglist", | |
| 169894 | + /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", | |
| 169895 | + /* 356 */ "nm ::= STRING", | |
| 169896 | + /* 357 */ "typetoken ::= typename", | |
| 169897 | + /* 358 */ "typename ::= ID|STRING", | |
| 169898 | + /* 359 */ "signed ::= plus_num", | |
| 169899 | + /* 360 */ "signed ::= minus_num", | |
| 169900 | + /* 361 */ "carglist ::= carglist ccons", | |
| 169901 | + /* 362 */ "carglist ::=", | |
| 169902 | + /* 363 */ "ccons ::= NULL onconf", | |
| 169903 | + /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", | |
| 169904 | + /* 365 */ "ccons ::= AS generated", | |
| 169905 | + /* 366 */ "conslist_opt ::= COMMA conslist", | |
| 169906 | + /* 367 */ "conslist ::= conslist tconscomma tcons", | |
| 169907 | + /* 368 */ "conslist ::= tcons", | |
| 169908 | + /* 369 */ "tconscomma ::=", | |
| 169909 | + /* 370 */ "defer_subclause_opt ::= defer_subclause", | |
| 169910 | + /* 371 */ "resolvetype ::= raisetype", | |
| 169911 | + /* 372 */ "selectnowith ::= oneselect", | |
| 169912 | + /* 373 */ "oneselect ::= values", | |
| 169913 | + /* 374 */ "sclp ::= selcollist COMMA", | |
| 169914 | + /* 375 */ "as ::= ID|STRING", | |
| 169915 | + /* 376 */ "indexed_opt ::= indexed_by", | |
| 169916 | + /* 377 */ "returning ::=", | |
| 169917 | + /* 378 */ "expr ::= term", | |
| 169918 | + /* 379 */ "likeop ::= LIKE_KW|MATCH", | |
| 169919 | + /* 380 */ "case_operand ::= expr", | |
| 169594 | 169920 | /* 381 */ "exprlist ::= nexprlist", |
| 169595 | 169921 | /* 382 */ "nmnum ::= plus_num", |
| 169596 | 169922 | /* 383 */ "nmnum ::= nm", |
| 169597 | 169923 | /* 384 */ "nmnum ::= ON", |
| 169598 | 169924 | /* 385 */ "nmnum ::= DELETE", |
| @@ -170346,162 +170672,162 @@ | ||
| 170346 | 170672 | 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 170347 | 170673 | 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 170348 | 170674 | 279, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 170349 | 170675 | 280, /* (227) case_else ::= ELSE expr */ |
| 170350 | 170676 | 280, /* (228) case_else ::= */ |
| 170351 | - 278, /* (229) case_operand ::= expr */ | |
| 170352 | - 278, /* (230) case_operand ::= */ | |
| 170353 | - 261, /* (231) exprlist ::= */ | |
| 170354 | - 253, /* (232) nexprlist ::= nexprlist COMMA expr */ | |
| 170355 | - 253, /* (233) nexprlist ::= expr */ | |
| 170356 | - 277, /* (234) paren_exprlist ::= */ | |
| 170357 | - 277, /* (235) paren_exprlist ::= LP exprlist RP */ | |
| 170358 | - 190, /* (236) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 170359 | - 281, /* (237) uniqueflag ::= UNIQUE */ | |
| 170360 | - 281, /* (238) uniqueflag ::= */ | |
| 170361 | - 221, /* (239) eidlist_opt ::= */ | |
| 170362 | - 221, /* (240) eidlist_opt ::= LP eidlist RP */ | |
| 170363 | - 232, /* (241) eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 170364 | - 232, /* (242) eidlist ::= nm collate sortorder */ | |
| 170365 | - 282, /* (243) collate ::= */ | |
| 170366 | - 282, /* (244) collate ::= COLLATE ID|STRING */ | |
| 170367 | - 190, /* (245) cmd ::= DROP INDEX ifexists fullname */ | |
| 170368 | - 190, /* (246) cmd ::= VACUUM vinto */ | |
| 170369 | - 190, /* (247) cmd ::= VACUUM nm vinto */ | |
| 170370 | - 283, /* (248) vinto ::= INTO expr */ | |
| 170371 | - 283, /* (249) vinto ::= */ | |
| 170372 | - 190, /* (250) cmd ::= PRAGMA nm dbnm */ | |
| 170373 | - 190, /* (251) cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 170374 | - 190, /* (252) cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 170375 | - 190, /* (253) cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 170376 | - 190, /* (254) cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 170377 | - 211, /* (255) plus_num ::= PLUS INTEGER|FLOAT */ | |
| 170378 | - 212, /* (256) minus_num ::= MINUS INTEGER|FLOAT */ | |
| 170379 | - 190, /* (257) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 170380 | - 285, /* (258) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 170381 | - 287, /* (259) trigger_time ::= BEFORE|AFTER */ | |
| 170382 | - 287, /* (260) trigger_time ::= INSTEAD OF */ | |
| 170383 | - 287, /* (261) trigger_time ::= */ | |
| 170384 | - 288, /* (262) trigger_event ::= DELETE|INSERT */ | |
| 170385 | - 288, /* (263) trigger_event ::= UPDATE */ | |
| 170386 | - 288, /* (264) trigger_event ::= UPDATE OF idlist */ | |
| 170387 | - 290, /* (265) when_clause ::= */ | |
| 170388 | - 290, /* (266) when_clause ::= WHEN expr */ | |
| 170389 | - 286, /* (267) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 170390 | - 286, /* (268) trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 170391 | - 292, /* (269) trnm ::= nm DOT nm */ | |
| 170392 | - 293, /* (270) tridxby ::= INDEXED BY nm */ | |
| 170393 | - 293, /* (271) tridxby ::= NOT INDEXED */ | |
| 170394 | - 291, /* (272) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 170395 | - 291, /* (273) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 170396 | - 291, /* (274) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 170397 | - 291, /* (275) trigger_cmd ::= scanpt select scanpt */ | |
| 170398 | - 217, /* (276) expr ::= RAISE LP IGNORE RP */ | |
| 170399 | - 217, /* (277) expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 170400 | - 236, /* (278) raisetype ::= ROLLBACK */ | |
| 170401 | - 236, /* (279) raisetype ::= ABORT */ | |
| 170402 | - 236, /* (280) raisetype ::= FAIL */ | |
| 170403 | - 190, /* (281) cmd ::= DROP TRIGGER ifexists fullname */ | |
| 170404 | - 190, /* (282) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 170405 | - 190, /* (283) cmd ::= DETACH database_kw_opt expr */ | |
| 170406 | - 295, /* (284) key_opt ::= */ | |
| 170407 | - 295, /* (285) key_opt ::= KEY expr */ | |
| 170408 | - 190, /* (286) cmd ::= REINDEX */ | |
| 170409 | - 190, /* (287) cmd ::= REINDEX nm dbnm */ | |
| 170410 | - 190, /* (288) cmd ::= ANALYZE */ | |
| 170411 | - 190, /* (289) cmd ::= ANALYZE nm dbnm */ | |
| 170412 | - 190, /* (290) cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 170413 | - 190, /* (291) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 170414 | - 190, /* (292) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 170415 | - 296, /* (293) add_column_fullname ::= fullname */ | |
| 170416 | - 190, /* (294) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 170417 | - 190, /* (295) cmd ::= create_vtab */ | |
| 170418 | - 190, /* (296) cmd ::= create_vtab LP vtabarglist RP */ | |
| 170419 | - 298, /* (297) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 170420 | - 300, /* (298) vtabarg ::= */ | |
| 170421 | - 301, /* (299) vtabargtoken ::= ANY */ | |
| 170422 | - 301, /* (300) vtabargtoken ::= lp anylist RP */ | |
| 170423 | - 302, /* (301) lp ::= LP */ | |
| 170424 | - 266, /* (302) with ::= WITH wqlist */ | |
| 170425 | - 266, /* (303) with ::= WITH RECURSIVE wqlist */ | |
| 170426 | - 305, /* (304) wqas ::= AS */ | |
| 170427 | - 305, /* (305) wqas ::= AS MATERIALIZED */ | |
| 170428 | - 305, /* (306) wqas ::= AS NOT MATERIALIZED */ | |
| 170429 | - 304, /* (307) wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 170430 | - 241, /* (308) wqlist ::= wqitem */ | |
| 170431 | - 241, /* (309) wqlist ::= wqlist COMMA wqitem */ | |
| 170432 | - 306, /* (310) windowdefn_list ::= windowdefn */ | |
| 170433 | - 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 170434 | - 307, /* (312) windowdefn ::= nm AS LP window RP */ | |
| 170435 | - 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170436 | - 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170437 | - 308, /* (315) window ::= ORDER BY sortlist frame_opt */ | |
| 170438 | - 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */ | |
| 170439 | - 308, /* (317) window ::= frame_opt */ | |
| 170440 | - 308, /* (318) window ::= nm frame_opt */ | |
| 170441 | - 309, /* (319) frame_opt ::= */ | |
| 170442 | - 309, /* (320) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 170443 | - 309, /* (321) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 170444 | - 313, /* (322) range_or_rows ::= RANGE|ROWS|GROUPS */ | |
| 170445 | - 315, /* (323) frame_bound_s ::= frame_bound */ | |
| 170446 | - 315, /* (324) frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 170447 | - 316, /* (325) frame_bound_e ::= frame_bound */ | |
| 170448 | - 316, /* (326) frame_bound_e ::= UNBOUNDED FOLLOWING */ | |
| 170449 | - 314, /* (327) frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 170450 | - 314, /* (328) frame_bound ::= CURRENT ROW */ | |
| 170451 | - 317, /* (329) frame_exclude_opt ::= */ | |
| 170452 | - 317, /* (330) frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 170453 | - 318, /* (331) frame_exclude ::= NO OTHERS */ | |
| 170454 | - 318, /* (332) frame_exclude ::= CURRENT ROW */ | |
| 170455 | - 318, /* (333) frame_exclude ::= GROUP|TIES */ | |
| 170456 | - 251, /* (334) window_clause ::= WINDOW windowdefn_list */ | |
| 170457 | - 273, /* (335) filter_over ::= filter_clause over_clause */ | |
| 170458 | - 273, /* (336) filter_over ::= over_clause */ | |
| 170459 | - 273, /* (337) filter_over ::= filter_clause */ | |
| 170460 | - 312, /* (338) over_clause ::= OVER LP window RP */ | |
| 170461 | - 312, /* (339) over_clause ::= OVER nm */ | |
| 170462 | - 311, /* (340) filter_clause ::= FILTER LP WHERE expr RP */ | |
| 170463 | - 185, /* (341) input ::= cmdlist */ | |
| 170464 | - 186, /* (342) cmdlist ::= cmdlist ecmd */ | |
| 170465 | - 186, /* (343) cmdlist ::= ecmd */ | |
| 170466 | - 187, /* (344) ecmd ::= SEMI */ | |
| 170467 | - 187, /* (345) ecmd ::= cmdx SEMI */ | |
| 170468 | - 187, /* (346) ecmd ::= explain cmdx SEMI */ | |
| 170469 | - 192, /* (347) trans_opt ::= */ | |
| 170470 | - 192, /* (348) trans_opt ::= TRANSACTION */ | |
| 170471 | - 192, /* (349) trans_opt ::= TRANSACTION nm */ | |
| 170472 | - 194, /* (350) savepoint_opt ::= SAVEPOINT */ | |
| 170473 | - 194, /* (351) savepoint_opt ::= */ | |
| 170474 | - 190, /* (352) cmd ::= create_table create_table_args */ | |
| 170475 | - 203, /* (353) table_option_set ::= table_option */ | |
| 170476 | - 201, /* (354) columnlist ::= columnlist COMMA columnname carglist */ | |
| 170477 | - 201, /* (355) columnlist ::= columnname carglist */ | |
| 170478 | - 193, /* (356) nm ::= ID|INDEXED|JOIN_KW */ | |
| 170479 | - 193, /* (357) nm ::= STRING */ | |
| 170480 | - 208, /* (358) typetoken ::= typename */ | |
| 170481 | - 209, /* (359) typename ::= ID|STRING */ | |
| 170482 | - 210, /* (360) signed ::= plus_num */ | |
| 170483 | - 210, /* (361) signed ::= minus_num */ | |
| 170484 | - 207, /* (362) carglist ::= carglist ccons */ | |
| 170485 | - 207, /* (363) carglist ::= */ | |
| 170486 | - 215, /* (364) ccons ::= NULL onconf */ | |
| 170487 | - 215, /* (365) ccons ::= GENERATED ALWAYS AS generated */ | |
| 170488 | - 215, /* (366) ccons ::= AS generated */ | |
| 170489 | - 202, /* (367) conslist_opt ::= COMMA conslist */ | |
| 170490 | - 228, /* (368) conslist ::= conslist tconscomma tcons */ | |
| 170491 | - 228, /* (369) conslist ::= tcons */ | |
| 170492 | - 229, /* (370) tconscomma ::= */ | |
| 170493 | - 233, /* (371) defer_subclause_opt ::= defer_subclause */ | |
| 170494 | - 235, /* (372) resolvetype ::= raisetype */ | |
| 170495 | - 239, /* (373) selectnowith ::= oneselect */ | |
| 170496 | - 240, /* (374) oneselect ::= values */ | |
| 170497 | - 254, /* (375) sclp ::= selcollist COMMA */ | |
| 170498 | - 255, /* (376) as ::= ID|STRING */ | |
| 170499 | - 264, /* (377) indexed_opt ::= indexed_by */ | |
| 170500 | - 272, /* (378) returning ::= */ | |
| 170501 | - 217, /* (379) expr ::= term */ | |
| 170502 | - 274, /* (380) likeop ::= LIKE_KW|MATCH */ | |
| 170677 | + 278, /* (229) case_operand ::= */ | |
| 170678 | + 261, /* (230) exprlist ::= */ | |
| 170679 | + 253, /* (231) nexprlist ::= nexprlist COMMA expr */ | |
| 170680 | + 253, /* (232) nexprlist ::= expr */ | |
| 170681 | + 277, /* (233) paren_exprlist ::= */ | |
| 170682 | + 277, /* (234) paren_exprlist ::= LP exprlist RP */ | |
| 170683 | + 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 170684 | + 281, /* (236) uniqueflag ::= UNIQUE */ | |
| 170685 | + 281, /* (237) uniqueflag ::= */ | |
| 170686 | + 221, /* (238) eidlist_opt ::= */ | |
| 170687 | + 221, /* (239) eidlist_opt ::= LP eidlist RP */ | |
| 170688 | + 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 170689 | + 232, /* (241) eidlist ::= nm collate sortorder */ | |
| 170690 | + 282, /* (242) collate ::= */ | |
| 170691 | + 282, /* (243) collate ::= COLLATE ID|STRING */ | |
| 170692 | + 190, /* (244) cmd ::= DROP INDEX ifexists fullname */ | |
| 170693 | + 190, /* (245) cmd ::= VACUUM vinto */ | |
| 170694 | + 190, /* (246) cmd ::= VACUUM nm vinto */ | |
| 170695 | + 283, /* (247) vinto ::= INTO expr */ | |
| 170696 | + 283, /* (248) vinto ::= */ | |
| 170697 | + 190, /* (249) cmd ::= PRAGMA nm dbnm */ | |
| 170698 | + 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 170699 | + 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 170700 | + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 170701 | + 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 170702 | + 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ | |
| 170703 | + 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ | |
| 170704 | + 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 170705 | + 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 170706 | + 287, /* (258) trigger_time ::= BEFORE|AFTER */ | |
| 170707 | + 287, /* (259) trigger_time ::= INSTEAD OF */ | |
| 170708 | + 287, /* (260) trigger_time ::= */ | |
| 170709 | + 288, /* (261) trigger_event ::= DELETE|INSERT */ | |
| 170710 | + 288, /* (262) trigger_event ::= UPDATE */ | |
| 170711 | + 288, /* (263) trigger_event ::= UPDATE OF idlist */ | |
| 170712 | + 290, /* (264) when_clause ::= */ | |
| 170713 | + 290, /* (265) when_clause ::= WHEN expr */ | |
| 170714 | + 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 170715 | + 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 170716 | + 292, /* (268) trnm ::= nm DOT nm */ | |
| 170717 | + 293, /* (269) tridxby ::= INDEXED BY nm */ | |
| 170718 | + 293, /* (270) tridxby ::= NOT INDEXED */ | |
| 170719 | + 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 170720 | + 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 170721 | + 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 170722 | + 291, /* (274) trigger_cmd ::= scanpt select scanpt */ | |
| 170723 | + 217, /* (275) expr ::= RAISE LP IGNORE RP */ | |
| 170724 | + 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 170725 | + 236, /* (277) raisetype ::= ROLLBACK */ | |
| 170726 | + 236, /* (278) raisetype ::= ABORT */ | |
| 170727 | + 236, /* (279) raisetype ::= FAIL */ | |
| 170728 | + 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ | |
| 170729 | + 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 170730 | + 190, /* (282) cmd ::= DETACH database_kw_opt expr */ | |
| 170731 | + 295, /* (283) key_opt ::= */ | |
| 170732 | + 295, /* (284) key_opt ::= KEY expr */ | |
| 170733 | + 190, /* (285) cmd ::= REINDEX */ | |
| 170734 | + 190, /* (286) cmd ::= REINDEX nm dbnm */ | |
| 170735 | + 190, /* (287) cmd ::= ANALYZE */ | |
| 170736 | + 190, /* (288) cmd ::= ANALYZE nm dbnm */ | |
| 170737 | + 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 170738 | + 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 170739 | + 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 170740 | + 296, /* (292) add_column_fullname ::= fullname */ | |
| 170741 | + 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 170742 | + 190, /* (294) cmd ::= create_vtab */ | |
| 170743 | + 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */ | |
| 170744 | + 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 170745 | + 300, /* (297) vtabarg ::= */ | |
| 170746 | + 301, /* (298) vtabargtoken ::= ANY */ | |
| 170747 | + 301, /* (299) vtabargtoken ::= lp anylist RP */ | |
| 170748 | + 302, /* (300) lp ::= LP */ | |
| 170749 | + 266, /* (301) with ::= WITH wqlist */ | |
| 170750 | + 266, /* (302) with ::= WITH RECURSIVE wqlist */ | |
| 170751 | + 305, /* (303) wqas ::= AS */ | |
| 170752 | + 305, /* (304) wqas ::= AS MATERIALIZED */ | |
| 170753 | + 305, /* (305) wqas ::= AS NOT MATERIALIZED */ | |
| 170754 | + 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 170755 | + 241, /* (307) wqlist ::= wqitem */ | |
| 170756 | + 241, /* (308) wqlist ::= wqlist COMMA wqitem */ | |
| 170757 | + 306, /* (309) windowdefn_list ::= windowdefn */ | |
| 170758 | + 306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 170759 | + 307, /* (311) windowdefn ::= nm AS LP window RP */ | |
| 170760 | + 308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170761 | + 308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170762 | + 308, /* (314) window ::= ORDER BY sortlist frame_opt */ | |
| 170763 | + 308, /* (315) window ::= nm ORDER BY sortlist frame_opt */ | |
| 170764 | + 308, /* (316) window ::= frame_opt */ | |
| 170765 | + 308, /* (317) window ::= nm frame_opt */ | |
| 170766 | + 309, /* (318) frame_opt ::= */ | |
| 170767 | + 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 170768 | + 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 170769 | + 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ | |
| 170770 | + 315, /* (322) frame_bound_s ::= frame_bound */ | |
| 170771 | + 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 170772 | + 316, /* (324) frame_bound_e ::= frame_bound */ | |
| 170773 | + 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ | |
| 170774 | + 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 170775 | + 314, /* (327) frame_bound ::= CURRENT ROW */ | |
| 170776 | + 317, /* (328) frame_exclude_opt ::= */ | |
| 170777 | + 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 170778 | + 318, /* (330) frame_exclude ::= NO OTHERS */ | |
| 170779 | + 318, /* (331) frame_exclude ::= CURRENT ROW */ | |
| 170780 | + 318, /* (332) frame_exclude ::= GROUP|TIES */ | |
| 170781 | + 251, /* (333) window_clause ::= WINDOW windowdefn_list */ | |
| 170782 | + 273, /* (334) filter_over ::= filter_clause over_clause */ | |
| 170783 | + 273, /* (335) filter_over ::= over_clause */ | |
| 170784 | + 273, /* (336) filter_over ::= filter_clause */ | |
| 170785 | + 312, /* (337) over_clause ::= OVER LP window RP */ | |
| 170786 | + 312, /* (338) over_clause ::= OVER nm */ | |
| 170787 | + 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ | |
| 170788 | + 185, /* (340) input ::= cmdlist */ | |
| 170789 | + 186, /* (341) cmdlist ::= cmdlist ecmd */ | |
| 170790 | + 186, /* (342) cmdlist ::= ecmd */ | |
| 170791 | + 187, /* (343) ecmd ::= SEMI */ | |
| 170792 | + 187, /* (344) ecmd ::= cmdx SEMI */ | |
| 170793 | + 187, /* (345) ecmd ::= explain cmdx SEMI */ | |
| 170794 | + 192, /* (346) trans_opt ::= */ | |
| 170795 | + 192, /* (347) trans_opt ::= TRANSACTION */ | |
| 170796 | + 192, /* (348) trans_opt ::= TRANSACTION nm */ | |
| 170797 | + 194, /* (349) savepoint_opt ::= SAVEPOINT */ | |
| 170798 | + 194, /* (350) savepoint_opt ::= */ | |
| 170799 | + 190, /* (351) cmd ::= create_table create_table_args */ | |
| 170800 | + 203, /* (352) table_option_set ::= table_option */ | |
| 170801 | + 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ | |
| 170802 | + 201, /* (354) columnlist ::= columnname carglist */ | |
| 170803 | + 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */ | |
| 170804 | + 193, /* (356) nm ::= STRING */ | |
| 170805 | + 208, /* (357) typetoken ::= typename */ | |
| 170806 | + 209, /* (358) typename ::= ID|STRING */ | |
| 170807 | + 210, /* (359) signed ::= plus_num */ | |
| 170808 | + 210, /* (360) signed ::= minus_num */ | |
| 170809 | + 207, /* (361) carglist ::= carglist ccons */ | |
| 170810 | + 207, /* (362) carglist ::= */ | |
| 170811 | + 215, /* (363) ccons ::= NULL onconf */ | |
| 170812 | + 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */ | |
| 170813 | + 215, /* (365) ccons ::= AS generated */ | |
| 170814 | + 202, /* (366) conslist_opt ::= COMMA conslist */ | |
| 170815 | + 228, /* (367) conslist ::= conslist tconscomma tcons */ | |
| 170816 | + 228, /* (368) conslist ::= tcons */ | |
| 170817 | + 229, /* (369) tconscomma ::= */ | |
| 170818 | + 233, /* (370) defer_subclause_opt ::= defer_subclause */ | |
| 170819 | + 235, /* (371) resolvetype ::= raisetype */ | |
| 170820 | + 239, /* (372) selectnowith ::= oneselect */ | |
| 170821 | + 240, /* (373) oneselect ::= values */ | |
| 170822 | + 254, /* (374) sclp ::= selcollist COMMA */ | |
| 170823 | + 255, /* (375) as ::= ID|STRING */ | |
| 170824 | + 264, /* (376) indexed_opt ::= indexed_by */ | |
| 170825 | + 272, /* (377) returning ::= */ | |
| 170826 | + 217, /* (378) expr ::= term */ | |
| 170827 | + 274, /* (379) likeop ::= LIKE_KW|MATCH */ | |
| 170828 | + 278, /* (380) case_operand ::= expr */ | |
| 170503 | 170829 | 261, /* (381) exprlist ::= nexprlist */ |
| 170504 | 170830 | 284, /* (382) nmnum ::= plus_num */ |
| 170505 | 170831 | 284, /* (383) nmnum ::= nm */ |
| 170506 | 170832 | 284, /* (384) nmnum ::= ON */ |
| 170507 | 170833 | 284, /* (385) nmnum ::= DELETE */ |
| @@ -170754,162 +171080,162 @@ | ||
| 170754 | 171080 | -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 170755 | 171081 | -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 170756 | 171082 | -4, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 170757 | 171083 | -2, /* (227) case_else ::= ELSE expr */ |
| 170758 | 171084 | 0, /* (228) case_else ::= */ |
| 170759 | - -1, /* (229) case_operand ::= expr */ | |
| 170760 | - 0, /* (230) case_operand ::= */ | |
| 170761 | - 0, /* (231) exprlist ::= */ | |
| 170762 | - -3, /* (232) nexprlist ::= nexprlist COMMA expr */ | |
| 170763 | - -1, /* (233) nexprlist ::= expr */ | |
| 170764 | - 0, /* (234) paren_exprlist ::= */ | |
| 170765 | - -3, /* (235) paren_exprlist ::= LP exprlist RP */ | |
| 170766 | - -12, /* (236) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 170767 | - -1, /* (237) uniqueflag ::= UNIQUE */ | |
| 170768 | - 0, /* (238) uniqueflag ::= */ | |
| 170769 | - 0, /* (239) eidlist_opt ::= */ | |
| 170770 | - -3, /* (240) eidlist_opt ::= LP eidlist RP */ | |
| 170771 | - -5, /* (241) eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 170772 | - -3, /* (242) eidlist ::= nm collate sortorder */ | |
| 170773 | - 0, /* (243) collate ::= */ | |
| 170774 | - -2, /* (244) collate ::= COLLATE ID|STRING */ | |
| 170775 | - -4, /* (245) cmd ::= DROP INDEX ifexists fullname */ | |
| 170776 | - -2, /* (246) cmd ::= VACUUM vinto */ | |
| 170777 | - -3, /* (247) cmd ::= VACUUM nm vinto */ | |
| 170778 | - -2, /* (248) vinto ::= INTO expr */ | |
| 170779 | - 0, /* (249) vinto ::= */ | |
| 170780 | - -3, /* (250) cmd ::= PRAGMA nm dbnm */ | |
| 170781 | - -5, /* (251) cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 170782 | - -6, /* (252) cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 170783 | - -5, /* (253) cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 170784 | - -6, /* (254) cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 170785 | - -2, /* (255) plus_num ::= PLUS INTEGER|FLOAT */ | |
| 170786 | - -2, /* (256) minus_num ::= MINUS INTEGER|FLOAT */ | |
| 170787 | - -5, /* (257) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 170788 | - -11, /* (258) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 170789 | - -1, /* (259) trigger_time ::= BEFORE|AFTER */ | |
| 170790 | - -2, /* (260) trigger_time ::= INSTEAD OF */ | |
| 170791 | - 0, /* (261) trigger_time ::= */ | |
| 170792 | - -1, /* (262) trigger_event ::= DELETE|INSERT */ | |
| 170793 | - -1, /* (263) trigger_event ::= UPDATE */ | |
| 170794 | - -3, /* (264) trigger_event ::= UPDATE OF idlist */ | |
| 170795 | - 0, /* (265) when_clause ::= */ | |
| 170796 | - -2, /* (266) when_clause ::= WHEN expr */ | |
| 170797 | - -3, /* (267) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 170798 | - -2, /* (268) trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 170799 | - -3, /* (269) trnm ::= nm DOT nm */ | |
| 170800 | - -3, /* (270) tridxby ::= INDEXED BY nm */ | |
| 170801 | - -2, /* (271) tridxby ::= NOT INDEXED */ | |
| 170802 | - -9, /* (272) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 170803 | - -8, /* (273) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 170804 | - -6, /* (274) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 170805 | - -3, /* (275) trigger_cmd ::= scanpt select scanpt */ | |
| 170806 | - -4, /* (276) expr ::= RAISE LP IGNORE RP */ | |
| 170807 | - -6, /* (277) expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 170808 | - -1, /* (278) raisetype ::= ROLLBACK */ | |
| 170809 | - -1, /* (279) raisetype ::= ABORT */ | |
| 170810 | - -1, /* (280) raisetype ::= FAIL */ | |
| 170811 | - -4, /* (281) cmd ::= DROP TRIGGER ifexists fullname */ | |
| 170812 | - -6, /* (282) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 170813 | - -3, /* (283) cmd ::= DETACH database_kw_opt expr */ | |
| 170814 | - 0, /* (284) key_opt ::= */ | |
| 170815 | - -2, /* (285) key_opt ::= KEY expr */ | |
| 170816 | - -1, /* (286) cmd ::= REINDEX */ | |
| 170817 | - -3, /* (287) cmd ::= REINDEX nm dbnm */ | |
| 170818 | - -1, /* (288) cmd ::= ANALYZE */ | |
| 170819 | - -3, /* (289) cmd ::= ANALYZE nm dbnm */ | |
| 170820 | - -6, /* (290) cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 170821 | - -7, /* (291) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 170822 | - -6, /* (292) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 170823 | - -1, /* (293) add_column_fullname ::= fullname */ | |
| 170824 | - -8, /* (294) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 170825 | - -1, /* (295) cmd ::= create_vtab */ | |
| 170826 | - -4, /* (296) cmd ::= create_vtab LP vtabarglist RP */ | |
| 170827 | - -8, /* (297) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 170828 | - 0, /* (298) vtabarg ::= */ | |
| 170829 | - -1, /* (299) vtabargtoken ::= ANY */ | |
| 170830 | - -3, /* (300) vtabargtoken ::= lp anylist RP */ | |
| 170831 | - -1, /* (301) lp ::= LP */ | |
| 170832 | - -2, /* (302) with ::= WITH wqlist */ | |
| 170833 | - -3, /* (303) with ::= WITH RECURSIVE wqlist */ | |
| 170834 | - -1, /* (304) wqas ::= AS */ | |
| 170835 | - -2, /* (305) wqas ::= AS MATERIALIZED */ | |
| 170836 | - -3, /* (306) wqas ::= AS NOT MATERIALIZED */ | |
| 170837 | - -6, /* (307) wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 170838 | - -1, /* (308) wqlist ::= wqitem */ | |
| 170839 | - -3, /* (309) wqlist ::= wqlist COMMA wqitem */ | |
| 170840 | - -1, /* (310) windowdefn_list ::= windowdefn */ | |
| 170841 | - -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 170842 | - -5, /* (312) windowdefn ::= nm AS LP window RP */ | |
| 170843 | - -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170844 | - -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 170845 | - -4, /* (315) window ::= ORDER BY sortlist frame_opt */ | |
| 170846 | - -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */ | |
| 170847 | - -1, /* (317) window ::= frame_opt */ | |
| 170848 | - -2, /* (318) window ::= nm frame_opt */ | |
| 170849 | - 0, /* (319) frame_opt ::= */ | |
| 170850 | - -3, /* (320) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 170851 | - -6, /* (321) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 170852 | - -1, /* (322) range_or_rows ::= RANGE|ROWS|GROUPS */ | |
| 170853 | - -1, /* (323) frame_bound_s ::= frame_bound */ | |
| 170854 | - -2, /* (324) frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 170855 | - -1, /* (325) frame_bound_e ::= frame_bound */ | |
| 170856 | - -2, /* (326) frame_bound_e ::= UNBOUNDED FOLLOWING */ | |
| 170857 | - -2, /* (327) frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 170858 | - -2, /* (328) frame_bound ::= CURRENT ROW */ | |
| 170859 | - 0, /* (329) frame_exclude_opt ::= */ | |
| 170860 | - -2, /* (330) frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 170861 | - -2, /* (331) frame_exclude ::= NO OTHERS */ | |
| 170862 | - -2, /* (332) frame_exclude ::= CURRENT ROW */ | |
| 170863 | - -1, /* (333) frame_exclude ::= GROUP|TIES */ | |
| 170864 | - -2, /* (334) window_clause ::= WINDOW windowdefn_list */ | |
| 170865 | - -2, /* (335) filter_over ::= filter_clause over_clause */ | |
| 170866 | - -1, /* (336) filter_over ::= over_clause */ | |
| 170867 | - -1, /* (337) filter_over ::= filter_clause */ | |
| 170868 | - -4, /* (338) over_clause ::= OVER LP window RP */ | |
| 170869 | - -2, /* (339) over_clause ::= OVER nm */ | |
| 170870 | - -5, /* (340) filter_clause ::= FILTER LP WHERE expr RP */ | |
| 170871 | - -1, /* (341) input ::= cmdlist */ | |
| 170872 | - -2, /* (342) cmdlist ::= cmdlist ecmd */ | |
| 170873 | - -1, /* (343) cmdlist ::= ecmd */ | |
| 170874 | - -1, /* (344) ecmd ::= SEMI */ | |
| 170875 | - -2, /* (345) ecmd ::= cmdx SEMI */ | |
| 170876 | - -3, /* (346) ecmd ::= explain cmdx SEMI */ | |
| 170877 | - 0, /* (347) trans_opt ::= */ | |
| 170878 | - -1, /* (348) trans_opt ::= TRANSACTION */ | |
| 170879 | - -2, /* (349) trans_opt ::= TRANSACTION nm */ | |
| 170880 | - -1, /* (350) savepoint_opt ::= SAVEPOINT */ | |
| 170881 | - 0, /* (351) savepoint_opt ::= */ | |
| 170882 | - -2, /* (352) cmd ::= create_table create_table_args */ | |
| 170883 | - -1, /* (353) table_option_set ::= table_option */ | |
| 170884 | - -4, /* (354) columnlist ::= columnlist COMMA columnname carglist */ | |
| 170885 | - -2, /* (355) columnlist ::= columnname carglist */ | |
| 170886 | - -1, /* (356) nm ::= ID|INDEXED|JOIN_KW */ | |
| 170887 | - -1, /* (357) nm ::= STRING */ | |
| 170888 | - -1, /* (358) typetoken ::= typename */ | |
| 170889 | - -1, /* (359) typename ::= ID|STRING */ | |
| 170890 | - -1, /* (360) signed ::= plus_num */ | |
| 170891 | - -1, /* (361) signed ::= minus_num */ | |
| 170892 | - -2, /* (362) carglist ::= carglist ccons */ | |
| 170893 | - 0, /* (363) carglist ::= */ | |
| 170894 | - -2, /* (364) ccons ::= NULL onconf */ | |
| 170895 | - -4, /* (365) ccons ::= GENERATED ALWAYS AS generated */ | |
| 170896 | - -2, /* (366) ccons ::= AS generated */ | |
| 170897 | - -2, /* (367) conslist_opt ::= COMMA conslist */ | |
| 170898 | - -3, /* (368) conslist ::= conslist tconscomma tcons */ | |
| 170899 | - -1, /* (369) conslist ::= tcons */ | |
| 170900 | - 0, /* (370) tconscomma ::= */ | |
| 170901 | - -1, /* (371) defer_subclause_opt ::= defer_subclause */ | |
| 170902 | - -1, /* (372) resolvetype ::= raisetype */ | |
| 170903 | - -1, /* (373) selectnowith ::= oneselect */ | |
| 170904 | - -1, /* (374) oneselect ::= values */ | |
| 170905 | - -2, /* (375) sclp ::= selcollist COMMA */ | |
| 170906 | - -1, /* (376) as ::= ID|STRING */ | |
| 170907 | - -1, /* (377) indexed_opt ::= indexed_by */ | |
| 170908 | - 0, /* (378) returning ::= */ | |
| 170909 | - -1, /* (379) expr ::= term */ | |
| 170910 | - -1, /* (380) likeop ::= LIKE_KW|MATCH */ | |
| 171085 | + 0, /* (229) case_operand ::= */ | |
| 171086 | + 0, /* (230) exprlist ::= */ | |
| 171087 | + -3, /* (231) nexprlist ::= nexprlist COMMA expr */ | |
| 171088 | + -1, /* (232) nexprlist ::= expr */ | |
| 171089 | + 0, /* (233) paren_exprlist ::= */ | |
| 171090 | + -3, /* (234) paren_exprlist ::= LP exprlist RP */ | |
| 171091 | + -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 171092 | + -1, /* (236) uniqueflag ::= UNIQUE */ | |
| 171093 | + 0, /* (237) uniqueflag ::= */ | |
| 171094 | + 0, /* (238) eidlist_opt ::= */ | |
| 171095 | + -3, /* (239) eidlist_opt ::= LP eidlist RP */ | |
| 171096 | + -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 171097 | + -3, /* (241) eidlist ::= nm collate sortorder */ | |
| 171098 | + 0, /* (242) collate ::= */ | |
| 171099 | + -2, /* (243) collate ::= COLLATE ID|STRING */ | |
| 171100 | + -4, /* (244) cmd ::= DROP INDEX ifexists fullname */ | |
| 171101 | + -2, /* (245) cmd ::= VACUUM vinto */ | |
| 171102 | + -3, /* (246) cmd ::= VACUUM nm vinto */ | |
| 171103 | + -2, /* (247) vinto ::= INTO expr */ | |
| 171104 | + 0, /* (248) vinto ::= */ | |
| 171105 | + -3, /* (249) cmd ::= PRAGMA nm dbnm */ | |
| 171106 | + -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 171107 | + -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 171108 | + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 171109 | + -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 171110 | + -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ | |
| 171111 | + -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ | |
| 171112 | + -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 171113 | + -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 171114 | + -1, /* (258) trigger_time ::= BEFORE|AFTER */ | |
| 171115 | + -2, /* (259) trigger_time ::= INSTEAD OF */ | |
| 171116 | + 0, /* (260) trigger_time ::= */ | |
| 171117 | + -1, /* (261) trigger_event ::= DELETE|INSERT */ | |
| 171118 | + -1, /* (262) trigger_event ::= UPDATE */ | |
| 171119 | + -3, /* (263) trigger_event ::= UPDATE OF idlist */ | |
| 171120 | + 0, /* (264) when_clause ::= */ | |
| 171121 | + -2, /* (265) when_clause ::= WHEN expr */ | |
| 171122 | + -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 171123 | + -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 171124 | + -3, /* (268) trnm ::= nm DOT nm */ | |
| 171125 | + -3, /* (269) tridxby ::= INDEXED BY nm */ | |
| 171126 | + -2, /* (270) tridxby ::= NOT INDEXED */ | |
| 171127 | + -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 171128 | + -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 171129 | + -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 171130 | + -3, /* (274) trigger_cmd ::= scanpt select scanpt */ | |
| 171131 | + -4, /* (275) expr ::= RAISE LP IGNORE RP */ | |
| 171132 | + -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 171133 | + -1, /* (277) raisetype ::= ROLLBACK */ | |
| 171134 | + -1, /* (278) raisetype ::= ABORT */ | |
| 171135 | + -1, /* (279) raisetype ::= FAIL */ | |
| 171136 | + -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ | |
| 171137 | + -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 171138 | + -3, /* (282) cmd ::= DETACH database_kw_opt expr */ | |
| 171139 | + 0, /* (283) key_opt ::= */ | |
| 171140 | + -2, /* (284) key_opt ::= KEY expr */ | |
| 171141 | + -1, /* (285) cmd ::= REINDEX */ | |
| 171142 | + -3, /* (286) cmd ::= REINDEX nm dbnm */ | |
| 171143 | + -1, /* (287) cmd ::= ANALYZE */ | |
| 171144 | + -3, /* (288) cmd ::= ANALYZE nm dbnm */ | |
| 171145 | + -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 171146 | + -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 171147 | + -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 171148 | + -1, /* (292) add_column_fullname ::= fullname */ | |
| 171149 | + -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 171150 | + -1, /* (294) cmd ::= create_vtab */ | |
| 171151 | + -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */ | |
| 171152 | + -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 171153 | + 0, /* (297) vtabarg ::= */ | |
| 171154 | + -1, /* (298) vtabargtoken ::= ANY */ | |
| 171155 | + -3, /* (299) vtabargtoken ::= lp anylist RP */ | |
| 171156 | + -1, /* (300) lp ::= LP */ | |
| 171157 | + -2, /* (301) with ::= WITH wqlist */ | |
| 171158 | + -3, /* (302) with ::= WITH RECURSIVE wqlist */ | |
| 171159 | + -1, /* (303) wqas ::= AS */ | |
| 171160 | + -2, /* (304) wqas ::= AS MATERIALIZED */ | |
| 171161 | + -3, /* (305) wqas ::= AS NOT MATERIALIZED */ | |
| 171162 | + -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 171163 | + -1, /* (307) wqlist ::= wqitem */ | |
| 171164 | + -3, /* (308) wqlist ::= wqlist COMMA wqitem */ | |
| 171165 | + -1, /* (309) windowdefn_list ::= windowdefn */ | |
| 171166 | + -3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 171167 | + -5, /* (311) windowdefn ::= nm AS LP window RP */ | |
| 171168 | + -5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 171169 | + -6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 171170 | + -4, /* (314) window ::= ORDER BY sortlist frame_opt */ | |
| 171171 | + -5, /* (315) window ::= nm ORDER BY sortlist frame_opt */ | |
| 171172 | + -1, /* (316) window ::= frame_opt */ | |
| 171173 | + -2, /* (317) window ::= nm frame_opt */ | |
| 171174 | + 0, /* (318) frame_opt ::= */ | |
| 171175 | + -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 171176 | + -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 171177 | + -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ | |
| 171178 | + -1, /* (322) frame_bound_s ::= frame_bound */ | |
| 171179 | + -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 171180 | + -1, /* (324) frame_bound_e ::= frame_bound */ | |
| 171181 | + -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ | |
| 171182 | + -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 171183 | + -2, /* (327) frame_bound ::= CURRENT ROW */ | |
| 171184 | + 0, /* (328) frame_exclude_opt ::= */ | |
| 171185 | + -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 171186 | + -2, /* (330) frame_exclude ::= NO OTHERS */ | |
| 171187 | + -2, /* (331) frame_exclude ::= CURRENT ROW */ | |
| 171188 | + -1, /* (332) frame_exclude ::= GROUP|TIES */ | |
| 171189 | + -2, /* (333) window_clause ::= WINDOW windowdefn_list */ | |
| 171190 | + -2, /* (334) filter_over ::= filter_clause over_clause */ | |
| 171191 | + -1, /* (335) filter_over ::= over_clause */ | |
| 171192 | + -1, /* (336) filter_over ::= filter_clause */ | |
| 171193 | + -4, /* (337) over_clause ::= OVER LP window RP */ | |
| 171194 | + -2, /* (338) over_clause ::= OVER nm */ | |
| 171195 | + -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ | |
| 171196 | + -1, /* (340) input ::= cmdlist */ | |
| 171197 | + -2, /* (341) cmdlist ::= cmdlist ecmd */ | |
| 171198 | + -1, /* (342) cmdlist ::= ecmd */ | |
| 171199 | + -1, /* (343) ecmd ::= SEMI */ | |
| 171200 | + -2, /* (344) ecmd ::= cmdx SEMI */ | |
| 171201 | + -3, /* (345) ecmd ::= explain cmdx SEMI */ | |
| 171202 | + 0, /* (346) trans_opt ::= */ | |
| 171203 | + -1, /* (347) trans_opt ::= TRANSACTION */ | |
| 171204 | + -2, /* (348) trans_opt ::= TRANSACTION nm */ | |
| 171205 | + -1, /* (349) savepoint_opt ::= SAVEPOINT */ | |
| 171206 | + 0, /* (350) savepoint_opt ::= */ | |
| 171207 | + -2, /* (351) cmd ::= create_table create_table_args */ | |
| 171208 | + -1, /* (352) table_option_set ::= table_option */ | |
| 171209 | + -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ | |
| 171210 | + -2, /* (354) columnlist ::= columnname carglist */ | |
| 171211 | + -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */ | |
| 171212 | + -1, /* (356) nm ::= STRING */ | |
| 171213 | + -1, /* (357) typetoken ::= typename */ | |
| 171214 | + -1, /* (358) typename ::= ID|STRING */ | |
| 171215 | + -1, /* (359) signed ::= plus_num */ | |
| 171216 | + -1, /* (360) signed ::= minus_num */ | |
| 171217 | + -2, /* (361) carglist ::= carglist ccons */ | |
| 171218 | + 0, /* (362) carglist ::= */ | |
| 171219 | + -2, /* (363) ccons ::= NULL onconf */ | |
| 171220 | + -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */ | |
| 171221 | + -2, /* (365) ccons ::= AS generated */ | |
| 171222 | + -2, /* (366) conslist_opt ::= COMMA conslist */ | |
| 171223 | + -3, /* (367) conslist ::= conslist tconscomma tcons */ | |
| 171224 | + -1, /* (368) conslist ::= tcons */ | |
| 171225 | + 0, /* (369) tconscomma ::= */ | |
| 171226 | + -1, /* (370) defer_subclause_opt ::= defer_subclause */ | |
| 171227 | + -1, /* (371) resolvetype ::= raisetype */ | |
| 171228 | + -1, /* (372) selectnowith ::= oneselect */ | |
| 171229 | + -1, /* (373) oneselect ::= values */ | |
| 171230 | + -2, /* (374) sclp ::= selcollist COMMA */ | |
| 171231 | + -1, /* (375) as ::= ID|STRING */ | |
| 171232 | + -1, /* (376) indexed_opt ::= indexed_by */ | |
| 171233 | + 0, /* (377) returning ::= */ | |
| 171234 | + -1, /* (378) expr ::= term */ | |
| 171235 | + -1, /* (379) likeop ::= LIKE_KW|MATCH */ | |
| 171236 | + -1, /* (380) case_operand ::= expr */ | |
| 170911 | 171237 | -1, /* (381) exprlist ::= nexprlist */ |
| 170912 | 171238 | -1, /* (382) nmnum ::= plus_num */ |
| 170913 | 171239 | -1, /* (383) nmnum ::= nm */ |
| 170914 | 171240 | -1, /* (384) nmnum ::= ON */ |
| 170915 | 171241 | -1, /* (385) nmnum ::= DELETE */ |
| @@ -170987,11 +171313,11 @@ | ||
| 170987 | 171313 | {yymsp[1].minor.yy394 = TK_DEFERRED;} |
| 170988 | 171314 | break; |
| 170989 | 171315 | case 5: /* transtype ::= DEFERRED */ |
| 170990 | 171316 | case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); |
| 170991 | 171317 | case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); |
| 170992 | - case 322: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==322); | |
| 171318 | + case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); | |
| 170993 | 171319 | {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} |
| 170994 | 171320 | break; |
| 170995 | 171321 | case 8: /* cmd ::= COMMIT|END trans_opt */ |
| 170996 | 171322 | case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); |
| 170997 | 171323 | {sqlite3EndTransaction(pParse,yymsp[-1].major);} |
| @@ -171024,11 +171350,11 @@ | ||
| 171024 | 171350 | case 47: /* autoinc ::= */ yytestcase(yyruleno==47); |
| 171025 | 171351 | case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); |
| 171026 | 171352 | case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); |
| 171027 | 171353 | case 81: /* ifexists ::= */ yytestcase(yyruleno==81); |
| 171028 | 171354 | case 98: /* distinct ::= */ yytestcase(yyruleno==98); |
| 171029 | - case 243: /* collate ::= */ yytestcase(yyruleno==243); | |
| 171355 | + case 242: /* collate ::= */ yytestcase(yyruleno==242); | |
| 171030 | 171356 | {yymsp[1].minor.yy394 = 0;} |
| 171031 | 171357 | break; |
| 171032 | 171358 | case 16: /* ifnotexists ::= IF NOT EXISTS */ |
| 171033 | 171359 | {yymsp[-2].minor.yy394 = 1;} |
| 171034 | 171360 | break; |
| @@ -171210,11 +171536,11 @@ | ||
| 171210 | 171536 | break; |
| 171211 | 171537 | case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ |
| 171212 | 171538 | case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); |
| 171213 | 171539 | case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215); |
| 171214 | 171540 | case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218); |
| 171215 | - case 244: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==244); | |
| 171541 | + case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243); | |
| 171216 | 171542 | {yymsp[-1].minor.yy394 = 1;} |
| 171217 | 171543 | break; |
| 171218 | 171544 | case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ |
| 171219 | 171545 | {yymsp[-1].minor.yy394 = 0;} |
| 171220 | 171546 | break; |
| @@ -171360,13 +171686,13 @@ | ||
| 171360 | 171686 | {yymsp[0].minor.yy394 = SF_All;} |
| 171361 | 171687 | break; |
| 171362 | 171688 | case 99: /* sclp ::= */ |
| 171363 | 171689 | case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); |
| 171364 | 171690 | case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); |
| 171365 | - case 231: /* exprlist ::= */ yytestcase(yyruleno==231); | |
| 171366 | - case 234: /* paren_exprlist ::= */ yytestcase(yyruleno==234); | |
| 171367 | - case 239: /* eidlist_opt ::= */ yytestcase(yyruleno==239); | |
| 171691 | + case 230: /* exprlist ::= */ yytestcase(yyruleno==230); | |
| 171692 | + case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233); | |
| 171693 | + case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238); | |
| 171368 | 171694 | {yymsp[1].minor.yy322 = 0;} |
| 171369 | 171695 | break; |
| 171370 | 171696 | case 100: /* selcollist ::= sclp scanpt expr scanpt as */ |
| 171371 | 171697 | { |
| 171372 | 171698 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); |
| @@ -171388,12 +171714,12 @@ | ||
| 171388 | 171714 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); |
| 171389 | 171715 | } |
| 171390 | 171716 | break; |
| 171391 | 171717 | case 103: /* as ::= AS nm */ |
| 171392 | 171718 | case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); |
| 171393 | - case 255: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==255); | |
| 171394 | - case 256: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==256); | |
| 171719 | + case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254); | |
| 171720 | + case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255); | |
| 171395 | 171721 | {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} |
| 171396 | 171722 | break; |
| 171397 | 171723 | case 105: /* from ::= */ |
| 171398 | 171724 | case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); |
| 171399 | 171725 | {yymsp[1].minor.yy131 = 0;} |
| @@ -171433,11 +171759,11 @@ | ||
| 171433 | 171759 | break; |
| 171434 | 171760 | case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ |
| 171435 | 171761 | { |
| 171436 | 171762 | if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ |
| 171437 | 171763 | yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; |
| 171438 | - }else if( yymsp[-3].minor.yy131->nSrc==1 ){ | |
| 171764 | + }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){ | |
| 171439 | 171765 | yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); |
| 171440 | 171766 | if( yymsp[-5].minor.yy131 ){ |
| 171441 | 171767 | SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; |
| 171442 | 171768 | SrcItem *pOld = yymsp[-3].minor.yy131->a; |
| 171443 | 171769 | pNew->zName = pOld->zName; |
| @@ -171562,19 +171888,19 @@ | ||
| 171562 | 171888 | case 144: /* having_opt ::= */ |
| 171563 | 171889 | case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); |
| 171564 | 171890 | case 151: /* where_opt ::= */ yytestcase(yyruleno==151); |
| 171565 | 171891 | case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); |
| 171566 | 171892 | case 228: /* case_else ::= */ yytestcase(yyruleno==228); |
| 171567 | - case 230: /* case_operand ::= */ yytestcase(yyruleno==230); | |
| 171568 | - case 249: /* vinto ::= */ yytestcase(yyruleno==249); | |
| 171893 | + case 229: /* case_operand ::= */ yytestcase(yyruleno==229); | |
| 171894 | + case 248: /* vinto ::= */ yytestcase(yyruleno==248); | |
| 171569 | 171895 | {yymsp[1].minor.yy528 = 0;} |
| 171570 | 171896 | break; |
| 171571 | 171897 | case 145: /* having_opt ::= HAVING expr */ |
| 171572 | 171898 | case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); |
| 171573 | 171899 | case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); |
| 171574 | 171900 | case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227); |
| 171575 | - case 248: /* vinto ::= INTO expr */ yytestcase(yyruleno==248); | |
| 171901 | + case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247); | |
| 171576 | 171902 | {yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} |
| 171577 | 171903 | break; |
| 171578 | 171904 | case 147: /* limit_opt ::= LIMIT expr */ |
| 171579 | 171905 | {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} |
| 171580 | 171906 | break; |
| @@ -172001,394 +172327,391 @@ | ||
| 172001 | 172327 | { |
| 172002 | 172328 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); |
| 172003 | 172329 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); |
| 172004 | 172330 | } |
| 172005 | 172331 | break; |
| 172006 | - case 229: /* case_operand ::= expr */ | |
| 172007 | -{yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*A-overwrites-X*/} | |
| 172008 | - break; | |
| 172009 | - case 232: /* nexprlist ::= nexprlist COMMA expr */ | |
| 172332 | + case 231: /* nexprlist ::= nexprlist COMMA expr */ | |
| 172010 | 172333 | {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} |
| 172011 | 172334 | break; |
| 172012 | - case 233: /* nexprlist ::= expr */ | |
| 172335 | + case 232: /* nexprlist ::= expr */ | |
| 172013 | 172336 | {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} |
| 172014 | 172337 | break; |
| 172015 | - case 235: /* paren_exprlist ::= LP exprlist RP */ | |
| 172016 | - case 240: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==240); | |
| 172338 | + case 234: /* paren_exprlist ::= LP exprlist RP */ | |
| 172339 | + case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239); | |
| 172017 | 172340 | {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} |
| 172018 | 172341 | break; |
| 172019 | - case 236: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 172342 | + case 235: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ | |
| 172020 | 172343 | { |
| 172021 | 172344 | sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, |
| 172022 | 172345 | sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, |
| 172023 | 172346 | &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); |
| 172024 | 172347 | if( IN_RENAME_OBJECT && pParse->pNewIndex ){ |
| 172025 | 172348 | sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); |
| 172026 | 172349 | } |
| 172027 | 172350 | } |
| 172028 | 172351 | break; |
| 172029 | - case 237: /* uniqueflag ::= UNIQUE */ | |
| 172030 | - case 279: /* raisetype ::= ABORT */ yytestcase(yyruleno==279); | |
| 172352 | + case 236: /* uniqueflag ::= UNIQUE */ | |
| 172353 | + case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278); | |
| 172031 | 172354 | {yymsp[0].minor.yy394 = OE_Abort;} |
| 172032 | 172355 | break; |
| 172033 | - case 238: /* uniqueflag ::= */ | |
| 172356 | + case 237: /* uniqueflag ::= */ | |
| 172034 | 172357 | {yymsp[1].minor.yy394 = OE_None;} |
| 172035 | 172358 | break; |
| 172036 | - case 241: /* eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 172359 | + case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */ | |
| 172037 | 172360 | { |
| 172038 | 172361 | yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); |
| 172039 | 172362 | } |
| 172040 | 172363 | break; |
| 172041 | - case 242: /* eidlist ::= nm collate sortorder */ | |
| 172364 | + case 241: /* eidlist ::= nm collate sortorder */ | |
| 172042 | 172365 | { |
| 172043 | 172366 | yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ |
| 172044 | 172367 | } |
| 172045 | 172368 | break; |
| 172046 | - case 245: /* cmd ::= DROP INDEX ifexists fullname */ | |
| 172369 | + case 244: /* cmd ::= DROP INDEX ifexists fullname */ | |
| 172047 | 172370 | {sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} |
| 172048 | 172371 | break; |
| 172049 | - case 246: /* cmd ::= VACUUM vinto */ | |
| 172372 | + case 245: /* cmd ::= VACUUM vinto */ | |
| 172050 | 172373 | {sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} |
| 172051 | 172374 | break; |
| 172052 | - case 247: /* cmd ::= VACUUM nm vinto */ | |
| 172375 | + case 246: /* cmd ::= VACUUM nm vinto */ | |
| 172053 | 172376 | {sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} |
| 172054 | 172377 | break; |
| 172055 | - case 250: /* cmd ::= PRAGMA nm dbnm */ | |
| 172378 | + case 249: /* cmd ::= PRAGMA nm dbnm */ | |
| 172056 | 172379 | {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} |
| 172057 | 172380 | break; |
| 172058 | - case 251: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 172381 | + case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ | |
| 172059 | 172382 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} |
| 172060 | 172383 | break; |
| 172061 | - case 252: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 172384 | + case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ | |
| 172062 | 172385 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} |
| 172063 | 172386 | break; |
| 172064 | - case 253: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 172387 | + case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ | |
| 172065 | 172388 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} |
| 172066 | 172389 | break; |
| 172067 | - case 254: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 172390 | + case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ | |
| 172068 | 172391 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} |
| 172069 | 172392 | break; |
| 172070 | - case 257: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 172393 | + case 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ | |
| 172071 | 172394 | { |
| 172072 | 172395 | Token all; |
| 172073 | 172396 | all.z = yymsp[-3].minor.yy0.z; |
| 172074 | 172397 | all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; |
| 172075 | 172398 | sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); |
| 172076 | 172399 | } |
| 172077 | 172400 | break; |
| 172078 | - case 258: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 172401 | + case 257: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ | |
| 172079 | 172402 | { |
| 172080 | 172403 | sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); |
| 172081 | 172404 | yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ |
| 172082 | 172405 | } |
| 172083 | 172406 | break; |
| 172084 | - case 259: /* trigger_time ::= BEFORE|AFTER */ | |
| 172407 | + case 258: /* trigger_time ::= BEFORE|AFTER */ | |
| 172085 | 172408 | { yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } |
| 172086 | 172409 | break; |
| 172087 | - case 260: /* trigger_time ::= INSTEAD OF */ | |
| 172410 | + case 259: /* trigger_time ::= INSTEAD OF */ | |
| 172088 | 172411 | { yymsp[-1].minor.yy394 = TK_INSTEAD;} |
| 172089 | 172412 | break; |
| 172090 | - case 261: /* trigger_time ::= */ | |
| 172413 | + case 260: /* trigger_time ::= */ | |
| 172091 | 172414 | { yymsp[1].minor.yy394 = TK_BEFORE; } |
| 172092 | 172415 | break; |
| 172093 | - case 262: /* trigger_event ::= DELETE|INSERT */ | |
| 172094 | - case 263: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==263); | |
| 172416 | + case 261: /* trigger_event ::= DELETE|INSERT */ | |
| 172417 | + case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262); | |
| 172095 | 172418 | {yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} |
| 172096 | 172419 | break; |
| 172097 | - case 264: /* trigger_event ::= UPDATE OF idlist */ | |
| 172420 | + case 263: /* trigger_event ::= UPDATE OF idlist */ | |
| 172098 | 172421 | {yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} |
| 172099 | 172422 | break; |
| 172100 | - case 265: /* when_clause ::= */ | |
| 172101 | - case 284: /* key_opt ::= */ yytestcase(yyruleno==284); | |
| 172423 | + case 264: /* when_clause ::= */ | |
| 172424 | + case 283: /* key_opt ::= */ yytestcase(yyruleno==283); | |
| 172102 | 172425 | { yymsp[1].minor.yy528 = 0; } |
| 172103 | 172426 | break; |
| 172104 | - case 266: /* when_clause ::= WHEN expr */ | |
| 172105 | - case 285: /* key_opt ::= KEY expr */ yytestcase(yyruleno==285); | |
| 172427 | + case 265: /* when_clause ::= WHEN expr */ | |
| 172428 | + case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284); | |
| 172106 | 172429 | { yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } |
| 172107 | 172430 | break; |
| 172108 | - case 267: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 172431 | + case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ | |
| 172109 | 172432 | { |
| 172110 | 172433 | assert( yymsp[-2].minor.yy33!=0 ); |
| 172111 | 172434 | yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; |
| 172112 | 172435 | yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172113 | 172436 | } |
| 172114 | 172437 | break; |
| 172115 | - case 268: /* trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 172438 | + case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */ | |
| 172116 | 172439 | { |
| 172117 | 172440 | assert( yymsp[-1].minor.yy33!=0 ); |
| 172118 | 172441 | yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172119 | 172442 | } |
| 172120 | 172443 | break; |
| 172121 | - case 269: /* trnm ::= nm DOT nm */ | |
| 172444 | + case 268: /* trnm ::= nm DOT nm */ | |
| 172122 | 172445 | { |
| 172123 | 172446 | yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; |
| 172124 | 172447 | sqlite3ErrorMsg(pParse, |
| 172125 | 172448 | "qualified table names are not allowed on INSERT, UPDATE, and DELETE " |
| 172126 | 172449 | "statements within triggers"); |
| 172127 | 172450 | } |
| 172128 | 172451 | break; |
| 172129 | - case 270: /* tridxby ::= INDEXED BY nm */ | |
| 172452 | + case 269: /* tridxby ::= INDEXED BY nm */ | |
| 172130 | 172453 | { |
| 172131 | 172454 | sqlite3ErrorMsg(pParse, |
| 172132 | 172455 | "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " |
| 172133 | 172456 | "within triggers"); |
| 172134 | 172457 | } |
| 172135 | 172458 | break; |
| 172136 | - case 271: /* tridxby ::= NOT INDEXED */ | |
| 172459 | + case 270: /* tridxby ::= NOT INDEXED */ | |
| 172137 | 172460 | { |
| 172138 | 172461 | sqlite3ErrorMsg(pParse, |
| 172139 | 172462 | "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " |
| 172140 | 172463 | "within triggers"); |
| 172141 | 172464 | } |
| 172142 | 172465 | break; |
| 172143 | - case 272: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 172466 | + case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ | |
| 172144 | 172467 | {yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172145 | 172468 | yymsp[-8].minor.yy33 = yylhsminor.yy33; |
| 172146 | 172469 | break; |
| 172147 | - case 273: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 172470 | + case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ | |
| 172148 | 172471 | { |
| 172149 | 172472 | yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ |
| 172150 | 172473 | } |
| 172151 | 172474 | yymsp[-7].minor.yy33 = yylhsminor.yy33; |
| 172152 | 172475 | break; |
| 172153 | - case 274: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 172476 | + case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ | |
| 172154 | 172477 | {yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172155 | 172478 | yymsp[-5].minor.yy33 = yylhsminor.yy33; |
| 172156 | 172479 | break; |
| 172157 | - case 275: /* trigger_cmd ::= scanpt select scanpt */ | |
| 172480 | + case 274: /* trigger_cmd ::= scanpt select scanpt */ | |
| 172158 | 172481 | {yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} |
| 172159 | 172482 | yymsp[-2].minor.yy33 = yylhsminor.yy33; |
| 172160 | 172483 | break; |
| 172161 | - case 276: /* expr ::= RAISE LP IGNORE RP */ | |
| 172484 | + case 275: /* expr ::= RAISE LP IGNORE RP */ | |
| 172162 | 172485 | { |
| 172163 | 172486 | yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); |
| 172164 | 172487 | if( yymsp[-3].minor.yy528 ){ |
| 172165 | 172488 | yymsp[-3].minor.yy528->affExpr = OE_Ignore; |
| 172166 | 172489 | } |
| 172167 | 172490 | } |
| 172168 | 172491 | break; |
| 172169 | - case 277: /* expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 172492 | + case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */ | |
| 172170 | 172493 | { |
| 172171 | 172494 | yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); |
| 172172 | 172495 | if( yymsp[-5].minor.yy528 ) { |
| 172173 | 172496 | yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; |
| 172174 | 172497 | } |
| 172175 | 172498 | } |
| 172176 | 172499 | break; |
| 172177 | - case 278: /* raisetype ::= ROLLBACK */ | |
| 172500 | + case 277: /* raisetype ::= ROLLBACK */ | |
| 172178 | 172501 | {yymsp[0].minor.yy394 = OE_Rollback;} |
| 172179 | 172502 | break; |
| 172180 | - case 280: /* raisetype ::= FAIL */ | |
| 172503 | + case 279: /* raisetype ::= FAIL */ | |
| 172181 | 172504 | {yymsp[0].minor.yy394 = OE_Fail;} |
| 172182 | 172505 | break; |
| 172183 | - case 281: /* cmd ::= DROP TRIGGER ifexists fullname */ | |
| 172506 | + case 280: /* cmd ::= DROP TRIGGER ifexists fullname */ | |
| 172184 | 172507 | { |
| 172185 | 172508 | sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); |
| 172186 | 172509 | } |
| 172187 | 172510 | break; |
| 172188 | - case 282: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 172511 | + case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ | |
| 172189 | 172512 | { |
| 172190 | 172513 | sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); |
| 172191 | 172514 | } |
| 172192 | 172515 | break; |
| 172193 | - case 283: /* cmd ::= DETACH database_kw_opt expr */ | |
| 172516 | + case 282: /* cmd ::= DETACH database_kw_opt expr */ | |
| 172194 | 172517 | { |
| 172195 | 172518 | sqlite3Detach(pParse, yymsp[0].minor.yy528); |
| 172196 | 172519 | } |
| 172197 | 172520 | break; |
| 172198 | - case 286: /* cmd ::= REINDEX */ | |
| 172521 | + case 285: /* cmd ::= REINDEX */ | |
| 172199 | 172522 | {sqlite3Reindex(pParse, 0, 0);} |
| 172200 | 172523 | break; |
| 172201 | - case 287: /* cmd ::= REINDEX nm dbnm */ | |
| 172524 | + case 286: /* cmd ::= REINDEX nm dbnm */ | |
| 172202 | 172525 | {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172203 | 172526 | break; |
| 172204 | - case 288: /* cmd ::= ANALYZE */ | |
| 172527 | + case 287: /* cmd ::= ANALYZE */ | |
| 172205 | 172528 | {sqlite3Analyze(pParse, 0, 0);} |
| 172206 | 172529 | break; |
| 172207 | - case 289: /* cmd ::= ANALYZE nm dbnm */ | |
| 172530 | + case 288: /* cmd ::= ANALYZE nm dbnm */ | |
| 172208 | 172531 | {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172209 | 172532 | break; |
| 172210 | - case 290: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 172533 | + case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ | |
| 172211 | 172534 | { |
| 172212 | 172535 | sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); |
| 172213 | 172536 | } |
| 172214 | 172537 | break; |
| 172215 | - case 291: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 172538 | + case 290: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ | |
| 172216 | 172539 | { |
| 172217 | 172540 | yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; |
| 172218 | 172541 | sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); |
| 172219 | 172542 | } |
| 172220 | 172543 | break; |
| 172221 | - case 292: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 172544 | + case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ | |
| 172222 | 172545 | { |
| 172223 | 172546 | sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); |
| 172224 | 172547 | } |
| 172225 | 172548 | break; |
| 172226 | - case 293: /* add_column_fullname ::= fullname */ | |
| 172549 | + case 292: /* add_column_fullname ::= fullname */ | |
| 172227 | 172550 | { |
| 172228 | 172551 | disableLookaside(pParse); |
| 172229 | 172552 | sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); |
| 172230 | 172553 | } |
| 172231 | 172554 | break; |
| 172232 | - case 294: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 172555 | + case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ | |
| 172233 | 172556 | { |
| 172234 | 172557 | sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); |
| 172235 | 172558 | } |
| 172236 | 172559 | break; |
| 172237 | - case 295: /* cmd ::= create_vtab */ | |
| 172560 | + case 294: /* cmd ::= create_vtab */ | |
| 172238 | 172561 | {sqlite3VtabFinishParse(pParse,0);} |
| 172239 | 172562 | break; |
| 172240 | - case 296: /* cmd ::= create_vtab LP vtabarglist RP */ | |
| 172563 | + case 295: /* cmd ::= create_vtab LP vtabarglist RP */ | |
| 172241 | 172564 | {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} |
| 172242 | 172565 | break; |
| 172243 | - case 297: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 172566 | + case 296: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ | |
| 172244 | 172567 | { |
| 172245 | 172568 | sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); |
| 172246 | 172569 | } |
| 172247 | 172570 | break; |
| 172248 | - case 298: /* vtabarg ::= */ | |
| 172571 | + case 297: /* vtabarg ::= */ | |
| 172249 | 172572 | {sqlite3VtabArgInit(pParse);} |
| 172250 | 172573 | break; |
| 172251 | - case 299: /* vtabargtoken ::= ANY */ | |
| 172252 | - case 300: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==300); | |
| 172253 | - case 301: /* lp ::= LP */ yytestcase(yyruleno==301); | |
| 172574 | + case 298: /* vtabargtoken ::= ANY */ | |
| 172575 | + case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299); | |
| 172576 | + case 300: /* lp ::= LP */ yytestcase(yyruleno==300); | |
| 172254 | 172577 | {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} |
| 172255 | 172578 | break; |
| 172256 | - case 302: /* with ::= WITH wqlist */ | |
| 172257 | - case 303: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==303); | |
| 172579 | + case 301: /* with ::= WITH wqlist */ | |
| 172580 | + case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302); | |
| 172258 | 172581 | { sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } |
| 172259 | 172582 | break; |
| 172260 | - case 304: /* wqas ::= AS */ | |
| 172583 | + case 303: /* wqas ::= AS */ | |
| 172261 | 172584 | {yymsp[0].minor.yy516 = M10d_Any;} |
| 172262 | 172585 | break; |
| 172263 | - case 305: /* wqas ::= AS MATERIALIZED */ | |
| 172586 | + case 304: /* wqas ::= AS MATERIALIZED */ | |
| 172264 | 172587 | {yymsp[-1].minor.yy516 = M10d_Yes;} |
| 172265 | 172588 | break; |
| 172266 | - case 306: /* wqas ::= AS NOT MATERIALIZED */ | |
| 172589 | + case 305: /* wqas ::= AS NOT MATERIALIZED */ | |
| 172267 | 172590 | {yymsp[-2].minor.yy516 = M10d_No;} |
| 172268 | 172591 | break; |
| 172269 | - case 307: /* wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 172592 | + case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */ | |
| 172270 | 172593 | { |
| 172271 | 172594 | yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ |
| 172272 | 172595 | } |
| 172273 | 172596 | break; |
| 172274 | - case 308: /* wqlist ::= wqitem */ | |
| 172597 | + case 307: /* wqlist ::= wqitem */ | |
| 172275 | 172598 | { |
| 172276 | 172599 | yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ |
| 172277 | 172600 | } |
| 172278 | 172601 | break; |
| 172279 | - case 309: /* wqlist ::= wqlist COMMA wqitem */ | |
| 172602 | + case 308: /* wqlist ::= wqlist COMMA wqitem */ | |
| 172280 | 172603 | { |
| 172281 | 172604 | yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); |
| 172282 | 172605 | } |
| 172283 | 172606 | break; |
| 172284 | - case 310: /* windowdefn_list ::= windowdefn */ | |
| 172607 | + case 309: /* windowdefn_list ::= windowdefn */ | |
| 172285 | 172608 | { yylhsminor.yy41 = yymsp[0].minor.yy41; } |
| 172286 | 172609 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172287 | 172610 | break; |
| 172288 | - case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 172611 | + case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ | |
| 172289 | 172612 | { |
| 172290 | 172613 | assert( yymsp[0].minor.yy41!=0 ); |
| 172291 | 172614 | sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); |
| 172292 | 172615 | yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; |
| 172293 | 172616 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172294 | 172617 | } |
| 172295 | 172618 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172296 | 172619 | break; |
| 172297 | - case 312: /* windowdefn ::= nm AS LP window RP */ | |
| 172620 | + case 311: /* windowdefn ::= nm AS LP window RP */ | |
| 172298 | 172621 | { |
| 172299 | 172622 | if( ALWAYS(yymsp[-1].minor.yy41) ){ |
| 172300 | 172623 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); |
| 172301 | 172624 | } |
| 172302 | 172625 | yylhsminor.yy41 = yymsp[-1].minor.yy41; |
| 172303 | 172626 | } |
| 172304 | 172627 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172305 | 172628 | break; |
| 172306 | - case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 172629 | + case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 172307 | 172630 | { |
| 172308 | 172631 | yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); |
| 172309 | 172632 | } |
| 172310 | 172633 | break; |
| 172311 | - case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 172634 | + case 313: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ | |
| 172312 | 172635 | { |
| 172313 | 172636 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); |
| 172314 | 172637 | } |
| 172315 | 172638 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172316 | 172639 | break; |
| 172317 | - case 315: /* window ::= ORDER BY sortlist frame_opt */ | |
| 172640 | + case 314: /* window ::= ORDER BY sortlist frame_opt */ | |
| 172318 | 172641 | { |
| 172319 | 172642 | yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); |
| 172320 | 172643 | } |
| 172321 | 172644 | break; |
| 172322 | - case 316: /* window ::= nm ORDER BY sortlist frame_opt */ | |
| 172645 | + case 315: /* window ::= nm ORDER BY sortlist frame_opt */ | |
| 172323 | 172646 | { |
| 172324 | 172647 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); |
| 172325 | 172648 | } |
| 172326 | 172649 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172327 | 172650 | break; |
| 172328 | - case 317: /* window ::= frame_opt */ | |
| 172329 | - case 336: /* filter_over ::= over_clause */ yytestcase(yyruleno==336); | |
| 172651 | + case 316: /* window ::= frame_opt */ | |
| 172652 | + case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335); | |
| 172330 | 172653 | { |
| 172331 | 172654 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172332 | 172655 | } |
| 172333 | 172656 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172334 | 172657 | break; |
| 172335 | - case 318: /* window ::= nm frame_opt */ | |
| 172658 | + case 317: /* window ::= nm frame_opt */ | |
| 172336 | 172659 | { |
| 172337 | 172660 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); |
| 172338 | 172661 | } |
| 172339 | 172662 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172340 | 172663 | break; |
| 172341 | - case 319: /* frame_opt ::= */ | |
| 172664 | + case 318: /* frame_opt ::= */ | |
| 172342 | 172665 | { |
| 172343 | 172666 | yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); |
| 172344 | 172667 | } |
| 172345 | 172668 | break; |
| 172346 | - case 320: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 172669 | + case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ | |
| 172347 | 172670 | { |
| 172348 | 172671 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); |
| 172349 | 172672 | } |
| 172350 | 172673 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172351 | 172674 | break; |
| 172352 | - case 321: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 172675 | + case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ | |
| 172353 | 172676 | { |
| 172354 | 172677 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); |
| 172355 | 172678 | } |
| 172356 | 172679 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172357 | 172680 | break; |
| 172358 | - case 323: /* frame_bound_s ::= frame_bound */ | |
| 172359 | - case 325: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==325); | |
| 172681 | + case 322: /* frame_bound_s ::= frame_bound */ | |
| 172682 | + case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); | |
| 172360 | 172683 | {yylhsminor.yy595 = yymsp[0].minor.yy595;} |
| 172361 | 172684 | yymsp[0].minor.yy595 = yylhsminor.yy595; |
| 172362 | 172685 | break; |
| 172363 | - case 324: /* frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 172364 | - case 326: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==326); | |
| 172365 | - case 328: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==328); | |
| 172686 | + case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ | |
| 172687 | + case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); | |
| 172688 | + case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); | |
| 172366 | 172689 | {yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} |
| 172367 | 172690 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172368 | 172691 | break; |
| 172369 | - case 327: /* frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 172692 | + case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ | |
| 172370 | 172693 | {yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} |
| 172371 | 172694 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172372 | 172695 | break; |
| 172373 | - case 329: /* frame_exclude_opt ::= */ | |
| 172696 | + case 328: /* frame_exclude_opt ::= */ | |
| 172374 | 172697 | {yymsp[1].minor.yy516 = 0;} |
| 172375 | 172698 | break; |
| 172376 | - case 330: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 172699 | + case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ | |
| 172377 | 172700 | {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} |
| 172378 | 172701 | break; |
| 172379 | - case 331: /* frame_exclude ::= NO OTHERS */ | |
| 172380 | - case 332: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==332); | |
| 172702 | + case 330: /* frame_exclude ::= NO OTHERS */ | |
| 172703 | + case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); | |
| 172381 | 172704 | {yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} |
| 172382 | 172705 | break; |
| 172383 | - case 333: /* frame_exclude ::= GROUP|TIES */ | |
| 172706 | + case 332: /* frame_exclude ::= GROUP|TIES */ | |
| 172384 | 172707 | {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} |
| 172385 | 172708 | break; |
| 172386 | - case 334: /* window_clause ::= WINDOW windowdefn_list */ | |
| 172709 | + case 333: /* window_clause ::= WINDOW windowdefn_list */ | |
| 172387 | 172710 | { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } |
| 172388 | 172711 | break; |
| 172389 | - case 335: /* filter_over ::= filter_clause over_clause */ | |
| 172712 | + case 334: /* filter_over ::= filter_clause over_clause */ | |
| 172390 | 172713 | { |
| 172391 | 172714 | if( yymsp[0].minor.yy41 ){ |
| 172392 | 172715 | yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; |
| 172393 | 172716 | }else{ |
| 172394 | 172717 | sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); |
| @@ -172395,11 +172718,11 @@ | ||
| 172395 | 172718 | } |
| 172396 | 172719 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172397 | 172720 | } |
| 172398 | 172721 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172399 | 172722 | break; |
| 172400 | - case 337: /* filter_over ::= filter_clause */ | |
| 172723 | + case 336: /* filter_over ::= filter_clause */ | |
| 172401 | 172724 | { |
| 172402 | 172725 | yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172403 | 172726 | if( yylhsminor.yy41 ){ |
| 172404 | 172727 | yylhsminor.yy41->eFrmType = TK_FILTER; |
| 172405 | 172728 | yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; |
| @@ -172407,68 +172730,69 @@ | ||
| 172407 | 172730 | sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); |
| 172408 | 172731 | } |
| 172409 | 172732 | } |
| 172410 | 172733 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172411 | 172734 | break; |
| 172412 | - case 338: /* over_clause ::= OVER LP window RP */ | |
| 172735 | + case 337: /* over_clause ::= OVER LP window RP */ | |
| 172413 | 172736 | { |
| 172414 | 172737 | yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; |
| 172415 | 172738 | assert( yymsp[-3].minor.yy41!=0 ); |
| 172416 | 172739 | } |
| 172417 | 172740 | break; |
| 172418 | - case 339: /* over_clause ::= OVER nm */ | |
| 172741 | + case 338: /* over_clause ::= OVER nm */ | |
| 172419 | 172742 | { |
| 172420 | 172743 | yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172421 | 172744 | if( yymsp[-1].minor.yy41 ){ |
| 172422 | 172745 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); |
| 172423 | 172746 | } |
| 172424 | 172747 | } |
| 172425 | 172748 | break; |
| 172426 | - case 340: /* filter_clause ::= FILTER LP WHERE expr RP */ | |
| 172749 | + case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ | |
| 172427 | 172750 | { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } |
| 172428 | 172751 | break; |
| 172429 | 172752 | default: |
| 172430 | - /* (341) input ::= cmdlist */ yytestcase(yyruleno==341); | |
| 172431 | - /* (342) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==342); | |
| 172432 | - /* (343) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=343); | |
| 172433 | - /* (344) ecmd ::= SEMI */ yytestcase(yyruleno==344); | |
| 172434 | - /* (345) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==345); | |
| 172435 | - /* (346) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=346); | |
| 172436 | - /* (347) trans_opt ::= */ yytestcase(yyruleno==347); | |
| 172437 | - /* (348) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==348); | |
| 172438 | - /* (349) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==349); | |
| 172439 | - /* (350) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==350); | |
| 172440 | - /* (351) savepoint_opt ::= */ yytestcase(yyruleno==351); | |
| 172441 | - /* (352) cmd ::= create_table create_table_args */ yytestcase(yyruleno==352); | |
| 172442 | - /* (353) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=353); | |
| 172443 | - /* (354) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==354); | |
| 172444 | - /* (355) columnlist ::= columnname carglist */ yytestcase(yyruleno==355); | |
| 172445 | - /* (356) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==356); | |
| 172446 | - /* (357) nm ::= STRING */ yytestcase(yyruleno==357); | |
| 172447 | - /* (358) typetoken ::= typename */ yytestcase(yyruleno==358); | |
| 172448 | - /* (359) typename ::= ID|STRING */ yytestcase(yyruleno==359); | |
| 172449 | - /* (360) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); | |
| 172450 | - /* (361) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=361); | |
| 172451 | - /* (362) carglist ::= carglist ccons */ yytestcase(yyruleno==362); | |
| 172452 | - /* (363) carglist ::= */ yytestcase(yyruleno==363); | |
| 172453 | - /* (364) ccons ::= NULL onconf */ yytestcase(yyruleno==364); | |
| 172454 | - /* (365) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==365); | |
| 172455 | - /* (366) ccons ::= AS generated */ yytestcase(yyruleno==366); | |
| 172456 | - /* (367) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==367); | |
| 172457 | - /* (368) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==368); | |
| 172458 | - /* (369) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=369); | |
| 172459 | - /* (370) tconscomma ::= */ yytestcase(yyruleno==370); | |
| 172460 | - /* (371) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=371); | |
| 172461 | - /* (372) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=372); | |
| 172462 | - /* (373) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=373); | |
| 172463 | - /* (374) oneselect ::= values */ yytestcase(yyruleno==374); | |
| 172464 | - /* (375) sclp ::= selcollist COMMA */ yytestcase(yyruleno==375); | |
| 172465 | - /* (376) as ::= ID|STRING */ yytestcase(yyruleno==376); | |
| 172466 | - /* (377) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=377); | |
| 172467 | - /* (378) returning ::= */ yytestcase(yyruleno==378); | |
| 172468 | - /* (379) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=379); | |
| 172469 | - /* (380) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==380); | |
| 172753 | + /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); | |
| 172754 | + /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); | |
| 172755 | + /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); | |
| 172756 | + /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); | |
| 172757 | + /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); | |
| 172758 | + /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); | |
| 172759 | + /* (346) trans_opt ::= */ yytestcase(yyruleno==346); | |
| 172760 | + /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); | |
| 172761 | + /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); | |
| 172762 | + /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); | |
| 172763 | + /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); | |
| 172764 | + /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); | |
| 172765 | + /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); | |
| 172766 | + /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); | |
| 172767 | + /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); | |
| 172768 | + /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355); | |
| 172769 | + /* (356) nm ::= STRING */ yytestcase(yyruleno==356); | |
| 172770 | + /* (357) typetoken ::= typename */ yytestcase(yyruleno==357); | |
| 172771 | + /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358); | |
| 172772 | + /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359); | |
| 172773 | + /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); | |
| 172774 | + /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361); | |
| 172775 | + /* (362) carglist ::= */ yytestcase(yyruleno==362); | |
| 172776 | + /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363); | |
| 172777 | + /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364); | |
| 172778 | + /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365); | |
| 172779 | + /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366); | |
| 172780 | + /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367); | |
| 172781 | + /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368); | |
| 172782 | + /* (369) tconscomma ::= */ yytestcase(yyruleno==369); | |
| 172783 | + /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370); | |
| 172784 | + /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371); | |
| 172785 | + /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372); | |
| 172786 | + /* (373) oneselect ::= values */ yytestcase(yyruleno==373); | |
| 172787 | + /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374); | |
| 172788 | + /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375); | |
| 172789 | + /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376); | |
| 172790 | + /* (377) returning ::= */ yytestcase(yyruleno==377); | |
| 172791 | + /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); | |
| 172792 | + /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); | |
| 172793 | + /* (380) case_operand ::= expr */ yytestcase(yyruleno==380); | |
| 172470 | 172794 | /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); |
| 172471 | 172795 | /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); |
| 172472 | 172796 | /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); |
| 172473 | 172797 | /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); |
| 172474 | 172798 | /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); |
| @@ -178496,11 +178820,11 @@ | ||
| 178496 | 178820 | if( pVfs==0 ) return 0; |
| 178497 | 178821 | |
| 178498 | 178822 | /* This function works in milliseconds, but the underlying OsSleep() |
| 178499 | 178823 | ** API uses microseconds. Hence the 1000's. |
| 178500 | 178824 | */ |
| 178501 | - rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); | |
| 178825 | + rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000); | |
| 178502 | 178826 | return rc; |
| 178503 | 178827 | } |
| 178504 | 178828 | |
| 178505 | 178829 | /* |
| 178506 | 178830 | ** Enable or disable the extended result codes. |
| @@ -199500,10 +199824,11 @@ | ||
| 199500 | 199824 | #define JNODE_REMOVE 0x04 /* Do not output */ |
| 199501 | 199825 | #define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ |
| 199502 | 199826 | #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ |
| 199503 | 199827 | #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ |
| 199504 | 199828 | #define JNODE_LABEL 0x40 /* Is a label of an object */ |
| 199829 | +#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */ | |
| 199505 | 199830 | |
| 199506 | 199831 | |
| 199507 | 199832 | /* A single node of parsed JSON |
| 199508 | 199833 | */ |
| 199509 | 199834 | struct JsonNode { |
| @@ -199526,25 +199851,27 @@ | ||
| 199526 | 199851 | u32 nNode; /* Number of slots of aNode[] used */ |
| 199527 | 199852 | u32 nAlloc; /* Number of slots of aNode[] allocated */ |
| 199528 | 199853 | JsonNode *aNode; /* Array of nodes containing the parse */ |
| 199529 | 199854 | const char *zJson; /* Original JSON string */ |
| 199530 | 199855 | u32 *aUp; /* Index of parent of each node */ |
| 199531 | - u8 oom; /* Set to true if out of memory */ | |
| 199856 | + u16 iDepth; /* Nesting depth */ | |
| 199532 | 199857 | u8 nErr; /* Number of errors seen */ |
| 199533 | - u16 iDepth; /* Nesting depth */ | |
| 199858 | + u8 oom; /* Set to true if out of memory */ | |
| 199859 | + u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ | |
| 199534 | 199860 | int nJson; /* Length of the zJson string in bytes */ |
| 199861 | + u32 iErr; /* Error location in zJson[] */ | |
| 199535 | 199862 | u32 iHold; /* Replace cache line with the lowest iHold value */ |
| 199536 | 199863 | }; |
| 199537 | 199864 | |
| 199538 | 199865 | /* |
| 199539 | 199866 | ** Maximum nesting depth of JSON for this implementation. |
| 199540 | 199867 | ** |
| 199541 | 199868 | ** This limit is needed to avoid a stack overflow in the recursive |
| 199542 | -** descent parser. A depth of 2000 is far deeper than any sane JSON | |
| 199543 | -** should go. | |
| 199869 | +** descent parser. A depth of 1000 is far deeper than any sane JSON | |
| 199870 | +** should go. Historical note: This limit was 2000 prior to version 3.42.0 | |
| 199544 | 199871 | */ |
| 199545 | -#define JSON_MAX_DEPTH 2000 | |
| 199872 | +#define JSON_MAX_DEPTH 1000 | |
| 199546 | 199873 | |
| 199547 | 199874 | /************************************************************************** |
| 199548 | 199875 | ** Utility routines for dealing with JsonString objects |
| 199549 | 199876 | **************************************************************************/ |
| 199550 | 199877 | |
| @@ -199689,10 +200016,133 @@ | ||
| 199689 | 200016 | p->zBuf[p->nUsed++] = c; |
| 199690 | 200017 | } |
| 199691 | 200018 | p->zBuf[p->nUsed++] = '"'; |
| 199692 | 200019 | assert( p->nUsed<p->nAlloc ); |
| 199693 | 200020 | } |
| 200021 | + | |
| 200022 | +/* | |
| 200023 | +** The zIn[0..N] string is a JSON5 string literal. Append to p a translation | |
| 200024 | +** of the string literal that standard JSON and that omits all JSON5 | |
| 200025 | +** features. | |
| 200026 | +*/ | |
| 200027 | +static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ | |
| 200028 | + u32 i; | |
| 200029 | + jsonAppendChar(p, '"'); | |
| 200030 | + zIn++; | |
| 200031 | + N -= 2; | |
| 200032 | + while( N>0 ){ | |
| 200033 | + for(i=0; i<N && zIn[i]!='\\'; i++){} | |
| 200034 | + if( i>0 ){ | |
| 200035 | + jsonAppendRaw(p, zIn, i); | |
| 200036 | + zIn += i; | |
| 200037 | + N -= i; | |
| 200038 | + if( N==0 ) break; | |
| 200039 | + } | |
| 200040 | + assert( zIn[0]=='\\' ); | |
| 200041 | + switch( (u8)zIn[1] ){ | |
| 200042 | + case '\'': | |
| 200043 | + jsonAppendChar(p, '\''); | |
| 200044 | + break; | |
| 200045 | + case 'v': | |
| 200046 | + jsonAppendRaw(p, "\\u0009", 6); | |
| 200047 | + break; | |
| 200048 | + case 'x': | |
| 200049 | + jsonAppendRaw(p, "\\u00", 4); | |
| 200050 | + jsonAppendRaw(p, &zIn[2], 2); | |
| 200051 | + zIn += 2; | |
| 200052 | + N -= 2; | |
| 200053 | + break; | |
| 200054 | + case '0': | |
| 200055 | + jsonAppendRaw(p, "\\u0000", 6); | |
| 200056 | + break; | |
| 200057 | + case '\r': | |
| 200058 | + if( zIn[2]=='\n' ){ | |
| 200059 | + zIn++; | |
| 200060 | + N--; | |
| 200061 | + } | |
| 200062 | + break; | |
| 200063 | + case '\n': | |
| 200064 | + break; | |
| 200065 | + case 0xe2: | |
| 200066 | + assert( N>=4 ); | |
| 200067 | + assert( 0x80==(u8)zIn[2] ); | |
| 200068 | + assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); | |
| 200069 | + zIn += 2; | |
| 200070 | + N -= 2; | |
| 200071 | + break; | |
| 200072 | + default: | |
| 200073 | + jsonAppendRaw(p, zIn, 2); | |
| 200074 | + break; | |
| 200075 | + } | |
| 200076 | + zIn += 2; | |
| 200077 | + N -= 2; | |
| 200078 | + } | |
| 200079 | + jsonAppendChar(p, '"'); | |
| 200080 | +} | |
| 200081 | + | |
| 200082 | +/* | |
| 200083 | +** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation | |
| 200084 | +** of the string literal that standard JSON and that omits all JSON5 | |
| 200085 | +** features. | |
| 200086 | +*/ | |
| 200087 | +static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ | |
| 200088 | + if( zIn[0]=='+' ){ | |
| 200089 | + zIn++; | |
| 200090 | + N--; | |
| 200091 | + }else if( zIn[0]=='-' ){ | |
| 200092 | + jsonAppendChar(p, '-'); | |
| 200093 | + zIn++; | |
| 200094 | + N--; | |
| 200095 | + } | |
| 200096 | + if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ | |
| 200097 | + sqlite3_int64 i = 0; | |
| 200098 | + int rc = sqlite3DecOrHexToI64(zIn, &i); | |
| 200099 | + if( rc<=1 ){ | |
| 200100 | + jsonPrintf(100,p,"%lld",i); | |
| 200101 | + }else{ | |
| 200102 | + assert( rc==2 ); | |
| 200103 | + jsonAppendRaw(p, "9.0e999", 7); | |
| 200104 | + } | |
| 200105 | + return; | |
| 200106 | + } | |
| 200107 | + jsonAppendRaw(p, zIn, N); | |
| 200108 | +} | |
| 200109 | + | |
| 200110 | +/* | |
| 200111 | +** The zIn[0..N] string is a JSON5 real literal. Append to p a translation | |
| 200112 | +** of the string literal that standard JSON and that omits all JSON5 | |
| 200113 | +** features. | |
| 200114 | +*/ | |
| 200115 | +static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ | |
| 200116 | + u32 i; | |
| 200117 | + if( zIn[0]=='+' ){ | |
| 200118 | + zIn++; | |
| 200119 | + N--; | |
| 200120 | + }else if( zIn[0]=='-' ){ | |
| 200121 | + jsonAppendChar(p, '-'); | |
| 200122 | + zIn++; | |
| 200123 | + N--; | |
| 200124 | + } | |
| 200125 | + if( zIn[0]=='.' ){ | |
| 200126 | + jsonAppendChar(p, '0'); | |
| 200127 | + } | |
| 200128 | + for(i=0; i<N; i++){ | |
| 200129 | + if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){ | |
| 200130 | + i++; | |
| 200131 | + jsonAppendRaw(p, zIn, i); | |
| 200132 | + zIn += i; | |
| 200133 | + N -= i; | |
| 200134 | + jsonAppendChar(p, '0'); | |
| 200135 | + break; | |
| 200136 | + } | |
| 200137 | + } | |
| 200138 | + if( N>0 ){ | |
| 200139 | + jsonAppendRaw(p, zIn, N); | |
| 200140 | + } | |
| 200141 | +} | |
| 200142 | + | |
| 200143 | + | |
| 199694 | 200144 | |
| 199695 | 200145 | /* |
| 199696 | 200146 | ** Append a function parameter value to the JSON string under |
| 199697 | 200147 | ** construction. |
| 199698 | 200148 | */ |
| @@ -199820,21 +200270,42 @@ | ||
| 199820 | 200270 | case JSON_FALSE: { |
| 199821 | 200271 | jsonAppendRaw(pOut, "false", 5); |
| 199822 | 200272 | break; |
| 199823 | 200273 | } |
| 199824 | 200274 | case JSON_STRING: { |
| 200275 | + assert( pNode->eU==1 ); | |
| 199825 | 200276 | if( pNode->jnFlags & JNODE_RAW ){ |
| 199826 | - assert( pNode->eU==1 ); | |
| 199827 | - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); | |
| 199828 | - break; | |
| 200277 | + if( pNode->jnFlags & JNODE_LABEL ){ | |
| 200278 | + jsonAppendChar(pOut, '"'); | |
| 200279 | + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); | |
| 200280 | + jsonAppendChar(pOut, '"'); | |
| 200281 | + }else{ | |
| 200282 | + jsonAppendString(pOut, pNode->u.zJContent, pNode->n); | |
| 200283 | + } | |
| 200284 | + }else if( pNode->jnFlags & JNODE_JSON5 ){ | |
| 200285 | + jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); | |
| 200286 | + }else{ | |
| 200287 | + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); | |
| 199829 | 200288 | } |
| 199830 | - /* no break */ deliberate_fall_through | |
| 200289 | + break; | |
| 199831 | 200290 | } |
| 199832 | - case JSON_REAL: | |
| 200291 | + case JSON_REAL: { | |
| 200292 | + assert( pNode->eU==1 ); | |
| 200293 | + if( pNode->jnFlags & JNODE_JSON5 ){ | |
| 200294 | + jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); | |
| 200295 | + }else{ | |
| 200296 | + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); | |
| 200297 | + } | |
| 200298 | + break; | |
| 200299 | + } | |
| 199833 | 200300 | case JSON_INT: { |
| 199834 | 200301 | assert( pNode->eU==1 ); |
| 199835 | - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); | |
| 200302 | + if( pNode->jnFlags & JNODE_JSON5 ){ | |
| 200303 | + jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); | |
| 200304 | + }else{ | |
| 200305 | + jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); | |
| 200306 | + } | |
| 199836 | 200307 | break; |
| 199837 | 200308 | } |
| 199838 | 200309 | case JSON_ARRAY: { |
| 199839 | 200310 | u32 j = 1; |
| 199840 | 200311 | jsonAppendChar(pOut, '['); |
| @@ -199946,63 +200417,45 @@ | ||
| 199946 | 200417 | sqlite3_result_int(pCtx, 0); |
| 199947 | 200418 | break; |
| 199948 | 200419 | } |
| 199949 | 200420 | case JSON_INT: { |
| 199950 | 200421 | sqlite3_int64 i = 0; |
| 200422 | + int rc; | |
| 200423 | + int bNeg = 0; | |
| 199951 | 200424 | const char *z; |
| 200425 | + | |
| 200426 | + | |
| 199952 | 200427 | assert( pNode->eU==1 ); |
| 199953 | 200428 | z = pNode->u.zJContent; |
| 199954 | - if( z[0]=='-' ){ z++; } | |
| 199955 | - while( z[0]>='0' && z[0]<='9' ){ | |
| 199956 | - unsigned v = *(z++) - '0'; | |
| 199957 | - if( i>=LARGEST_INT64/10 ){ | |
| 199958 | - if( i>LARGEST_INT64/10 ) goto int_as_real; | |
| 199959 | - if( z[0]>='0' && z[0]<='9' ) goto int_as_real; | |
| 199960 | - if( v==9 ) goto int_as_real; | |
| 199961 | - if( v==8 ){ | |
| 199962 | - if( pNode->u.zJContent[0]=='-' ){ | |
| 199963 | - sqlite3_result_int64(pCtx, SMALLEST_INT64); | |
| 199964 | - goto int_done; | |
| 199965 | - }else{ | |
| 199966 | - goto int_as_real; | |
| 199967 | - } | |
| 199968 | - } | |
| 199969 | - } | |
| 199970 | - i = i*10 + v; | |
| 199971 | - } | |
| 199972 | - if( pNode->u.zJContent[0]=='-' ){ i = -i; } | |
| 199973 | - sqlite3_result_int64(pCtx, i); | |
| 199974 | - int_done: | |
| 199975 | - break; | |
| 199976 | - int_as_real: ; /* no break */ deliberate_fall_through | |
| 200429 | + if( z[0]=='-' ){ z++; bNeg = 1; } | |
| 200430 | + else if( z[0]=='+' ){ z++; } | |
| 200431 | + rc = sqlite3DecOrHexToI64(z, &i); | |
| 200432 | + if( rc<=1 ){ | |
| 200433 | + sqlite3_result_int64(pCtx, bNeg ? -i : i); | |
| 200434 | + }else if( rc==3 && bNeg ){ | |
| 200435 | + sqlite3_result_int64(pCtx, SMALLEST_INT64); | |
| 200436 | + }else{ | |
| 200437 | + goto to_double; | |
| 200438 | + } | |
| 200439 | + break; | |
| 199977 | 200440 | } |
| 199978 | 200441 | case JSON_REAL: { |
| 199979 | 200442 | double r; |
| 199980 | -#ifdef SQLITE_AMALGAMATION | |
| 199981 | 200443 | const char *z; |
| 199982 | 200444 | assert( pNode->eU==1 ); |
| 200445 | + to_double: | |
| 199983 | 200446 | z = pNode->u.zJContent; |
| 199984 | 200447 | sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); |
| 199985 | -#else | |
| 199986 | - assert( pNode->eU==1 ); | |
| 199987 | - r = strtod(pNode->u.zJContent, 0); | |
| 199988 | -#endif | |
| 199989 | 200448 | sqlite3_result_double(pCtx, r); |
| 199990 | 200449 | break; |
| 199991 | 200450 | } |
| 199992 | 200451 | case JSON_STRING: { |
| 199993 | -#if 0 /* Never happens because JNODE_RAW is only set by json_set(), | |
| 199994 | - ** json_insert() and json_replace() and those routines do not | |
| 199995 | - ** call jsonReturn() */ | |
| 199996 | 200452 | if( pNode->jnFlags & JNODE_RAW ){ |
| 199997 | 200453 | assert( pNode->eU==1 ); |
| 199998 | 200454 | sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, |
| 199999 | 200455 | SQLITE_TRANSIENT); |
| 200000 | - }else | |
| 200001 | -#endif | |
| 200002 | - assert( (pNode->jnFlags & JNODE_RAW)==0 ); | |
| 200003 | - if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ | |
| 200456 | + }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ | |
| 200004 | 200457 | /* JSON formatted without any backslash-escapes */ |
| 200005 | 200458 | assert( pNode->eU==1 ); |
| 200006 | 200459 | sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, |
| 200007 | 200460 | SQLITE_TRANSIENT); |
| 200008 | 200461 | }else{ |
| @@ -200010,22 +200463,21 @@ | ||
| 200010 | 200463 | u32 i; |
| 200011 | 200464 | u32 n = pNode->n; |
| 200012 | 200465 | const char *z; |
| 200013 | 200466 | char *zOut; |
| 200014 | 200467 | u32 j; |
| 200468 | + u32 nOut = n; | |
| 200015 | 200469 | assert( pNode->eU==1 ); |
| 200016 | 200470 | z = pNode->u.zJContent; |
| 200017 | - zOut = sqlite3_malloc( n+1 ); | |
| 200471 | + zOut = sqlite3_malloc( nOut+1 ); | |
| 200018 | 200472 | if( zOut==0 ){ |
| 200019 | 200473 | sqlite3_result_error_nomem(pCtx); |
| 200020 | 200474 | break; |
| 200021 | 200475 | } |
| 200022 | 200476 | for(i=1, j=0; i<n-1; i++){ |
| 200023 | 200477 | char c = z[i]; |
| 200024 | - if( c!='\\' ){ | |
| 200025 | - zOut[j++] = c; | |
| 200026 | - }else{ | |
| 200478 | + if( c=='\\' ){ | |
| 200027 | 200479 | c = z[++i]; |
| 200028 | 200480 | if( c=='u' ){ |
| 200029 | 200481 | u32 v = jsonHexToInt4(z+i+1); |
| 200030 | 200482 | i += 4; |
| 200031 | 200483 | if( v==0 ) break; |
| @@ -200053,26 +200505,44 @@ | ||
| 200053 | 200505 | zOut[j++] = 0xe0 | (v>>12); |
| 200054 | 200506 | zOut[j++] = 0x80 | ((v>>6)&0x3f); |
| 200055 | 200507 | zOut[j++] = 0x80 | (v&0x3f); |
| 200056 | 200508 | } |
| 200057 | 200509 | } |
| 200510 | + continue; | |
| 200511 | + }else if( c=='b' ){ | |
| 200512 | + c = '\b'; | |
| 200513 | + }else if( c=='f' ){ | |
| 200514 | + c = '\f'; | |
| 200515 | + }else if( c=='n' ){ | |
| 200516 | + c = '\n'; | |
| 200517 | + }else if( c=='r' ){ | |
| 200518 | + c = '\r'; | |
| 200519 | + }else if( c=='t' ){ | |
| 200520 | + c = '\t'; | |
| 200521 | + }else if( c=='v' ){ | |
| 200522 | + c = '\v'; | |
| 200523 | + }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ | |
| 200524 | + /* pass through unchanged */ | |
| 200525 | + }else if( c=='0' ){ | |
| 200526 | + c = 0; | |
| 200527 | + }else if( c=='x' ){ | |
| 200528 | + c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); | |
| 200529 | + i += 2; | |
| 200530 | + }else if( c=='\r' && z[i+1]=='\n' ){ | |
| 200531 | + i++; | |
| 200532 | + continue; | |
| 200533 | + }else if( 0xe2==(u8)c ){ | |
| 200534 | + assert( 0x80==(u8)z[i+1] ); | |
| 200535 | + assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); | |
| 200536 | + i += 2; | |
| 200537 | + continue; | |
| 200058 | 200538 | }else{ |
| 200059 | - if( c=='b' ){ | |
| 200060 | - c = '\b'; | |
| 200061 | - }else if( c=='f' ){ | |
| 200062 | - c = '\f'; | |
| 200063 | - }else if( c=='n' ){ | |
| 200064 | - c = '\n'; | |
| 200065 | - }else if( c=='r' ){ | |
| 200066 | - c = '\r'; | |
| 200067 | - }else if( c=='t' ){ | |
| 200068 | - c = '\t'; | |
| 200069 | - } | |
| 200070 | - zOut[j++] = c; | |
| 200071 | - } | |
| 200072 | - } | |
| 200073 | - } | |
| 200539 | + continue; | |
| 200540 | + } | |
| 200541 | + } /* end if( c=='\\' ) */ | |
| 200542 | + zOut[j++] = c; | |
| 200543 | + } /* end for() */ | |
| 200074 | 200544 | zOut[j] = 0; |
| 200075 | 200545 | sqlite3_result_text(pCtx, zOut, j, sqlite3_free); |
| 200076 | 200546 | } |
| 200077 | 200547 | break; |
| 200078 | 200548 | } |
| @@ -200136,28 +200606,159 @@ | ||
| 200136 | 200606 | JsonNode *p; |
| 200137 | 200607 | if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ |
| 200138 | 200608 | return jsonParseAddNodeExpand(pParse, eType, n, zContent); |
| 200139 | 200609 | } |
| 200140 | 200610 | p = &pParse->aNode[pParse->nNode]; |
| 200141 | - p->eType = (u8)eType; | |
| 200142 | - p->jnFlags = 0; | |
| 200611 | + p->eType = (u8)(eType & 0xff); | |
| 200612 | + p->jnFlags = (u8)(eType >> 8); | |
| 200143 | 200613 | VVA( p->eU = zContent ? 1 : 0 ); |
| 200144 | 200614 | p->n = n; |
| 200145 | 200615 | p->u.zJContent = zContent; |
| 200146 | 200616 | return pParse->nNode++; |
| 200147 | 200617 | } |
| 200618 | + | |
| 200619 | +/* | |
| 200620 | +** Return true if z[] begins with 2 (or more) hexadecimal digits | |
| 200621 | +*/ | |
| 200622 | +static int jsonIs2Hex(const char *z){ | |
| 200623 | + return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); | |
| 200624 | +} | |
| 200148 | 200625 | |
| 200149 | 200626 | /* |
| 200150 | 200627 | ** Return true if z[] begins with 4 (or more) hexadecimal digits |
| 200151 | 200628 | */ |
| 200152 | 200629 | static int jsonIs4Hex(const char *z){ |
| 200153 | - int i; | |
| 200154 | - for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; | |
| 200155 | - return 1; | |
| 200630 | + return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); | |
| 200156 | 200631 | } |
| 200157 | 200632 | |
| 200158 | -#ifdef SQLITE_ENABLE_JSON_NAN_INF | |
| 200633 | +/* | |
| 200634 | +** Return the number of bytes of JSON5 whitespace at the beginning of | |
| 200635 | +** the input string z[]. | |
| 200636 | +** | |
| 200637 | +** JSON5 whitespace consists of any of the following characters: | |
| 200638 | +** | |
| 200639 | +** Unicode UTF-8 Name | |
| 200640 | +** U+0009 09 horizontal tab | |
| 200641 | +** U+000a 0a line feed | |
| 200642 | +** U+000b 0b vertical tab | |
| 200643 | +** U+000c 0c form feed | |
| 200644 | +** U+000d 0d carriage return | |
| 200645 | +** U+0020 20 space | |
| 200646 | +** U+00a0 c2 a0 non-breaking space | |
| 200647 | +** U+1680 e1 9a 80 ogham space mark | |
| 200648 | +** U+2000 e2 80 80 en quad | |
| 200649 | +** U+2001 e2 80 81 em quad | |
| 200650 | +** U+2002 e2 80 82 en space | |
| 200651 | +** U+2003 e2 80 83 em space | |
| 200652 | +** U+2004 e2 80 84 three-per-em space | |
| 200653 | +** U+2005 e2 80 85 four-per-em space | |
| 200654 | +** U+2006 e2 80 86 six-per-em space | |
| 200655 | +** U+2007 e2 80 87 figure space | |
| 200656 | +** U+2008 e2 80 88 punctuation space | |
| 200657 | +** U+2009 e2 80 89 thin space | |
| 200658 | +** U+200a e2 80 8a hair space | |
| 200659 | +** U+2028 e2 80 a8 line separator | |
| 200660 | +** U+2029 e2 80 a9 paragraph separator | |
| 200661 | +** U+202f e2 80 af narrow no-break space (NNBSP) | |
| 200662 | +** U+205f e2 81 9f medium mathematical space (MMSP) | |
| 200663 | +** U+3000 e3 80 80 ideographical space | |
| 200664 | +** U+FEFF ef bb bf byte order mark | |
| 200665 | +** | |
| 200666 | +** In addition, comments between '/', '*' and '*', '/' and | |
| 200667 | +** from '/', '/' to end-of-line are also considered to be whitespace. | |
| 200668 | +*/ | |
| 200669 | +static int json5Whitespace(const char *zIn){ | |
| 200670 | + int n = 0; | |
| 200671 | + const u8 *z = (u8*)zIn; | |
| 200672 | + while( 1 /*exit by "goto whitespace_done"*/ ){ | |
| 200673 | + switch( z[n] ){ | |
| 200674 | + case 0x09: | |
| 200675 | + case 0x0a: | |
| 200676 | + case 0x0b: | |
| 200677 | + case 0x0c: | |
| 200678 | + case 0x0d: | |
| 200679 | + case 0x20: { | |
| 200680 | + n++; | |
| 200681 | + break; | |
| 200682 | + } | |
| 200683 | + case '/': { | |
| 200684 | + if( z[n+1]=='*' && z[n+2]!=0 ){ | |
| 200685 | + int j; | |
| 200686 | + for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ | |
| 200687 | + if( z[j]==0 ) goto whitespace_done; | |
| 200688 | + } | |
| 200689 | + n = j+1; | |
| 200690 | + break; | |
| 200691 | + }else if( z[n+1]=='/' ){ | |
| 200692 | + int j; | |
| 200693 | + char c; | |
| 200694 | + for(j=n+2; (c = z[j])!=0; j++){ | |
| 200695 | + if( c=='\n' || c=='\r' ) break; | |
| 200696 | + if( 0xe2==(u8)c && 0x80==(u8)z[j+1] | |
| 200697 | + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) | |
| 200698 | + ){ | |
| 200699 | + j += 2; | |
| 200700 | + break; | |
| 200701 | + } | |
| 200702 | + } | |
| 200703 | + n = j; | |
| 200704 | + if( z[n] ) n++; | |
| 200705 | + break; | |
| 200706 | + } | |
| 200707 | + goto whitespace_done; | |
| 200708 | + } | |
| 200709 | + case 0xc2: { | |
| 200710 | + if( z[n+1]==0xa0 ){ | |
| 200711 | + n += 2; | |
| 200712 | + break; | |
| 200713 | + } | |
| 200714 | + goto whitespace_done; | |
| 200715 | + } | |
| 200716 | + case 0xe1: { | |
| 200717 | + if( z[n+1]==0x9a && z[n+2]==0x80 ){ | |
| 200718 | + n += 3; | |
| 200719 | + break; | |
| 200720 | + } | |
| 200721 | + goto whitespace_done; | |
| 200722 | + } | |
| 200723 | + case 0xe2: { | |
| 200724 | + if( z[n+1]==0x80 ){ | |
| 200725 | + u8 c = z[n+2]; | |
| 200726 | + if( c<0x80 ) goto whitespace_done; | |
| 200727 | + if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ | |
| 200728 | + n += 3; | |
| 200729 | + break; | |
| 200730 | + } | |
| 200731 | + }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ | |
| 200732 | + n += 3; | |
| 200733 | + break; | |
| 200734 | + } | |
| 200735 | + goto whitespace_done; | |
| 200736 | + } | |
| 200737 | + case 0xe3: { | |
| 200738 | + if( z[n+1]==0x80 && z[n+2]==0x80 ){ | |
| 200739 | + n += 3; | |
| 200740 | + break; | |
| 200741 | + } | |
| 200742 | + goto whitespace_done; | |
| 200743 | + } | |
| 200744 | + case 0xef: { | |
| 200745 | + if( z[n+1]==0xbb && z[n+2]==0xbf ){ | |
| 200746 | + n += 3; | |
| 200747 | + break; | |
| 200748 | + } | |
| 200749 | + goto whitespace_done; | |
| 200750 | + } | |
| 200751 | + default: { | |
| 200752 | + goto whitespace_done; | |
| 200753 | + } | |
| 200754 | + } | |
| 200755 | + } | |
| 200756 | + whitespace_done: | |
| 200757 | + return n; | |
| 200758 | +} | |
| 200759 | + | |
| 200159 | 200760 | /* |
| 200160 | 200761 | ** Extra floating-point literals to allow in JSON. |
| 200161 | 200762 | */ |
| 200162 | 200763 | static const struct NanInfName { |
| 200163 | 200764 | char c1; |
| @@ -200172,204 +200773,455 @@ | ||
| 200172 | 200773 | { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, |
| 200173 | 200774 | { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, |
| 200174 | 200775 | { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, |
| 200175 | 200776 | { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, |
| 200176 | 200777 | }; |
| 200177 | -#endif /* SQLITE_ENABLE_JSON_NAN_INF */ | |
| 200178 | 200778 | |
| 200179 | 200779 | /* |
| 200180 | 200780 | ** Parse a single JSON value which begins at pParse->zJson[i]. Return the |
| 200181 | 200781 | ** index of the first character past the end of the value parsed. |
| 200182 | 200782 | ** |
| 200183 | -** Return negative for a syntax error. Special cases: return -2 if the | |
| 200184 | -** first non-whitespace character is '}' and return -3 if the first | |
| 200185 | -** non-whitespace character is ']'. | |
| 200783 | +** Special return values: | |
| 200784 | +** | |
| 200785 | +** 0 End if input | |
| 200786 | +** -1 Syntax error | |
| 200787 | +** -2 '}' seen | |
| 200788 | +** -3 ']' seen | |
| 200789 | +** -4 ',' seen | |
| 200790 | +** -5 ':' seen | |
| 200186 | 200791 | */ |
| 200187 | 200792 | static int jsonParseValue(JsonParse *pParse, u32 i){ |
| 200188 | 200793 | char c; |
| 200189 | 200794 | u32 j; |
| 200190 | 200795 | int iThis; |
| 200191 | 200796 | int x; |
| 200192 | 200797 | JsonNode *pNode; |
| 200193 | 200798 | const char *z = pParse->zJson; |
| 200194 | - while( fast_isspace(z[i]) ){ i++; } | |
| 200195 | - if( (c = z[i])=='{' ){ | |
| 200799 | +json_parse_restart: | |
| 200800 | + switch( (u8)z[i] ){ | |
| 200801 | + case '{': { | |
| 200196 | 200802 | /* Parse object */ |
| 200197 | 200803 | iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); |
| 200198 | 200804 | if( iThis<0 ) return -1; |
| 200805 | + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ | |
| 200806 | + pParse->iErr = i; | |
| 200807 | + return -1; | |
| 200808 | + } | |
| 200199 | 200809 | for(j=i+1;;j++){ |
| 200200 | - while( fast_isspace(z[j]) ){ j++; } | |
| 200201 | - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; | |
| 200810 | + u32 nNode = pParse->nNode; | |
| 200202 | 200811 | x = jsonParseValue(pParse, j); |
| 200203 | - if( x<0 ){ | |
| 200204 | - pParse->iDepth--; | |
| 200205 | - if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; | |
| 200206 | - return -1; | |
| 200812 | + if( x<=0 ){ | |
| 200813 | + if( x==(-2) ){ | |
| 200814 | + j = pParse->iErr; | |
| 200815 | + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; | |
| 200816 | + break; | |
| 200817 | + } | |
| 200818 | + j += json5Whitespace(&z[j]); | |
| 200819 | + if( sqlite3JsonId1(z[j]) | |
| 200820 | + || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) | |
| 200821 | + ){ | |
| 200822 | + int k = j+1; | |
| 200823 | + while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) | |
| 200824 | + || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) | |
| 200825 | + ){ | |
| 200826 | + k++; | |
| 200827 | + } | |
| 200828 | + jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); | |
| 200829 | + pParse->hasNonstd = 1; | |
| 200830 | + x = k; | |
| 200831 | + }else{ | |
| 200832 | + if( x!=-1 ) pParse->iErr = j; | |
| 200833 | + return -1; | |
| 200834 | + } | |
| 200207 | 200835 | } |
| 200208 | 200836 | if( pParse->oom ) return -1; |
| 200209 | - pNode = &pParse->aNode[pParse->nNode-1]; | |
| 200210 | - if( pNode->eType!=JSON_STRING ) return -1; | |
| 200837 | + pNode = &pParse->aNode[nNode]; | |
| 200838 | + if( pNode->eType!=JSON_STRING ){ | |
| 200839 | + pParse->iErr = j; | |
| 200840 | + return -1; | |
| 200841 | + } | |
| 200211 | 200842 | pNode->jnFlags |= JNODE_LABEL; |
| 200212 | 200843 | j = x; |
| 200213 | - while( fast_isspace(z[j]) ){ j++; } | |
| 200214 | - if( z[j]!=':' ) return -1; | |
| 200215 | - j++; | |
| 200844 | + if( z[j]==':' ){ | |
| 200845 | + j++; | |
| 200846 | + }else{ | |
| 200847 | + if( fast_isspace(z[j]) ){ | |
| 200848 | + do{ j++; }while( fast_isspace(z[j]) ); | |
| 200849 | + if( z[j]==':' ){ | |
| 200850 | + j++; | |
| 200851 | + goto parse_object_value; | |
| 200852 | + } | |
| 200853 | + } | |
| 200854 | + x = jsonParseValue(pParse, j); | |
| 200855 | + if( x!=(-5) ){ | |
| 200856 | + if( x!=(-1) ) pParse->iErr = j; | |
| 200857 | + return -1; | |
| 200858 | + } | |
| 200859 | + j = pParse->iErr+1; | |
| 200860 | + } | |
| 200861 | + parse_object_value: | |
| 200216 | 200862 | x = jsonParseValue(pParse, j); |
| 200217 | - pParse->iDepth--; | |
| 200218 | - if( x<0 ) return -1; | |
| 200863 | + if( x<=0 ){ | |
| 200864 | + if( x!=(-1) ) pParse->iErr = j; | |
| 200865 | + return -1; | |
| 200866 | + } | |
| 200219 | 200867 | j = x; |
| 200220 | - while( fast_isspace(z[j]) ){ j++; } | |
| 200221 | - c = z[j]; | |
| 200222 | - if( c==',' ) continue; | |
| 200223 | - if( c!='}' ) return -1; | |
| 200224 | - break; | |
| 200868 | + if( z[j]==',' ){ | |
| 200869 | + continue; | |
| 200870 | + }else if( z[j]=='}' ){ | |
| 200871 | + break; | |
| 200872 | + }else{ | |
| 200873 | + if( fast_isspace(z[j]) ){ | |
| 200874 | + do{ j++; }while( fast_isspace(z[j]) ); | |
| 200875 | + if( z[j]==',' ){ | |
| 200876 | + continue; | |
| 200877 | + }else if( z[j]=='}' ){ | |
| 200878 | + break; | |
| 200879 | + } | |
| 200880 | + } | |
| 200881 | + x = jsonParseValue(pParse, j); | |
| 200882 | + if( x==(-4) ){ | |
| 200883 | + j = pParse->iErr; | |
| 200884 | + continue; | |
| 200885 | + } | |
| 200886 | + if( x==(-2) ){ | |
| 200887 | + j = pParse->iErr; | |
| 200888 | + break; | |
| 200889 | + } | |
| 200890 | + } | |
| 200891 | + pParse->iErr = j; | |
| 200892 | + return -1; | |
| 200225 | 200893 | } |
| 200226 | 200894 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200895 | + pParse->iDepth--; | |
| 200227 | 200896 | return j+1; |
| 200228 | - }else if( c=='[' ){ | |
| 200897 | + } | |
| 200898 | + case '[': { | |
| 200229 | 200899 | /* Parse array */ |
| 200230 | 200900 | iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); |
| 200231 | 200901 | if( iThis<0 ) return -1; |
| 200902 | + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ | |
| 200903 | + pParse->iErr = i; | |
| 200904 | + return -1; | |
| 200905 | + } | |
| 200232 | 200906 | memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); |
| 200233 | 200907 | for(j=i+1;;j++){ |
| 200234 | - while( fast_isspace(z[j]) ){ j++; } | |
| 200235 | - if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; | |
| 200236 | 200908 | x = jsonParseValue(pParse, j); |
| 200237 | - pParse->iDepth--; | |
| 200238 | - if( x<0 ){ | |
| 200239 | - if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; | |
| 200909 | + if( x<=0 ){ | |
| 200910 | + if( x==(-3) ){ | |
| 200911 | + j = pParse->iErr; | |
| 200912 | + if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; | |
| 200913 | + break; | |
| 200914 | + } | |
| 200915 | + if( x!=(-1) ) pParse->iErr = j; | |
| 200240 | 200916 | return -1; |
| 200241 | 200917 | } |
| 200242 | 200918 | j = x; |
| 200243 | - while( fast_isspace(z[j]) ){ j++; } | |
| 200244 | - c = z[j]; | |
| 200245 | - if( c==',' ) continue; | |
| 200246 | - if( c!=']' ) return -1; | |
| 200247 | - break; | |
| 200919 | + if( z[j]==',' ){ | |
| 200920 | + continue; | |
| 200921 | + }else if( z[j]==']' ){ | |
| 200922 | + break; | |
| 200923 | + }else{ | |
| 200924 | + if( fast_isspace(z[j]) ){ | |
| 200925 | + do{ j++; }while( fast_isspace(z[j]) ); | |
| 200926 | + if( z[j]==',' ){ | |
| 200927 | + continue; | |
| 200928 | + }else if( z[j]==']' ){ | |
| 200929 | + break; | |
| 200930 | + } | |
| 200931 | + } | |
| 200932 | + x = jsonParseValue(pParse, j); | |
| 200933 | + if( x==(-4) ){ | |
| 200934 | + j = pParse->iErr; | |
| 200935 | + continue; | |
| 200936 | + } | |
| 200937 | + if( x==(-3) ){ | |
| 200938 | + j = pParse->iErr; | |
| 200939 | + break; | |
| 200940 | + } | |
| 200941 | + } | |
| 200942 | + pParse->iErr = j; | |
| 200943 | + return -1; | |
| 200248 | 200944 | } |
| 200249 | 200945 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200946 | + pParse->iDepth--; | |
| 200250 | 200947 | return j+1; |
| 200251 | - }else if( c=='"' ){ | |
| 200948 | + } | |
| 200949 | + case '\'': { | |
| 200950 | + u8 jnFlags; | |
| 200951 | + char cDelim; | |
| 200952 | + pParse->hasNonstd = 1; | |
| 200953 | + jnFlags = JNODE_JSON5; | |
| 200954 | + goto parse_string; | |
| 200955 | + case '"': | |
| 200252 | 200956 | /* Parse string */ |
| 200253 | - u8 jnFlags = 0; | |
| 200957 | + jnFlags = 0; | |
| 200958 | + parse_string: | |
| 200959 | + cDelim = z[i]; | |
| 200254 | 200960 | j = i+1; |
| 200255 | 200961 | for(;;){ |
| 200256 | 200962 | c = z[j]; |
| 200257 | 200963 | if( (c & ~0x1f)==0 ){ |
| 200258 | 200964 | /* Control characters are not allowed in strings */ |
| 200965 | + pParse->iErr = j; | |
| 200259 | 200966 | return -1; |
| 200260 | 200967 | } |
| 200261 | 200968 | if( c=='\\' ){ |
| 200262 | 200969 | c = z[++j]; |
| 200263 | 200970 | if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' |
| 200264 | 200971 | || c=='n' || c=='r' || c=='t' |
| 200265 | - || (c=='u' && jsonIs4Hex(z+j+1)) ){ | |
| 200266 | - jnFlags = JNODE_ESCAPE; | |
| 200972 | + || (c=='u' && jsonIs4Hex(&z[j+1])) ){ | |
| 200973 | + jnFlags |= JNODE_ESCAPE; | |
| 200974 | + }else if( c=='\'' || c=='0' || c=='v' || c=='\n' | |
| 200975 | + || (0xe2==(u8)c && 0x80==(u8)z[j+1] | |
| 200976 | + && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) | |
| 200977 | + || (c=='x' && jsonIs2Hex(&z[j+1])) ){ | |
| 200978 | + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); | |
| 200979 | + pParse->hasNonstd = 1; | |
| 200980 | + }else if( c=='\r' ){ | |
| 200981 | + if( z[j+1]=='\n' ) j++; | |
| 200982 | + jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); | |
| 200983 | + pParse->hasNonstd = 1; | |
| 200267 | 200984 | }else{ |
| 200985 | + pParse->iErr = j; | |
| 200268 | 200986 | return -1; |
| 200269 | 200987 | } |
| 200270 | - }else if( c=='"' ){ | |
| 200988 | + }else if( c==cDelim ){ | |
| 200271 | 200989 | break; |
| 200272 | 200990 | } |
| 200273 | 200991 | j++; |
| 200274 | 200992 | } |
| 200275 | - jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); | |
| 200276 | - if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; | |
| 200993 | + jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); | |
| 200277 | 200994 | return j+1; |
| 200278 | - }else if( c=='n' | |
| 200279 | - && strncmp(z+i,"null",4)==0 | |
| 200280 | - && !sqlite3Isalnum(z[i+4]) ){ | |
| 200281 | - jsonParseAddNode(pParse, JSON_NULL, 0, 0); | |
| 200282 | - return i+4; | |
| 200283 | - }else if( c=='t' | |
| 200284 | - && strncmp(z+i,"true",4)==0 | |
| 200285 | - && !sqlite3Isalnum(z[i+4]) ){ | |
| 200286 | - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); | |
| 200287 | - return i+4; | |
| 200288 | - }else if( c=='f' | |
| 200289 | - && strncmp(z+i,"false",5)==0 | |
| 200290 | - && !sqlite3Isalnum(z[i+5]) ){ | |
| 200291 | - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); | |
| 200292 | - return i+5; | |
| 200293 | - }else if( c=='-' || (c>='0' && c<='9') ){ | |
| 200995 | + } | |
| 200996 | + case 't': { | |
| 200997 | + if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ | |
| 200998 | + jsonParseAddNode(pParse, JSON_TRUE, 0, 0); | |
| 200999 | + return i+4; | |
| 201000 | + } | |
| 201001 | + pParse->iErr = i; | |
| 201002 | + return -1; | |
| 201003 | + } | |
| 201004 | + case 'f': { | |
| 201005 | + if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ | |
| 201006 | + jsonParseAddNode(pParse, JSON_FALSE, 0, 0); | |
| 201007 | + return i+5; | |
| 201008 | + } | |
| 201009 | + pParse->iErr = i; | |
| 201010 | + return -1; | |
| 201011 | + } | |
| 201012 | + case '+': { | |
| 201013 | + u8 seenDP, seenE, jnFlags; | |
| 201014 | + pParse->hasNonstd = 1; | |
| 201015 | + jnFlags = JNODE_JSON5; | |
| 201016 | + goto parse_number; | |
| 201017 | + case '.': | |
| 201018 | + if( sqlite3Isdigit(z[i+1]) ){ | |
| 201019 | + pParse->hasNonstd = 1; | |
| 201020 | + jnFlags = JNODE_JSON5; | |
| 201021 | + seenE = 0; | |
| 201022 | + seenDP = JSON_REAL; | |
| 201023 | + goto parse_number_2; | |
| 201024 | + } | |
| 201025 | + pParse->iErr = i; | |
| 201026 | + return -1; | |
| 201027 | + case '-': | |
| 201028 | + case '0': | |
| 201029 | + case '1': | |
| 201030 | + case '2': | |
| 201031 | + case '3': | |
| 201032 | + case '4': | |
| 201033 | + case '5': | |
| 201034 | + case '6': | |
| 201035 | + case '7': | |
| 201036 | + case '8': | |
| 201037 | + case '9': | |
| 200294 | 201038 | /* Parse number */ |
| 200295 | - u8 seenDP = 0; | |
| 200296 | - u8 seenE = 0; | |
| 201039 | + jnFlags = 0; | |
| 201040 | + parse_number: | |
| 201041 | + seenDP = JSON_INT; | |
| 201042 | + seenE = 0; | |
| 200297 | 201043 | assert( '-' < '0' ); |
| 201044 | + assert( '+' < '0' ); | |
| 201045 | + assert( '.' < '0' ); | |
| 201046 | + c = z[i]; | |
| 201047 | + | |
| 200298 | 201048 | if( c<='0' ){ |
| 200299 | - j = c=='-' ? i+1 : i; | |
| 200300 | - if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; | |
| 201049 | + if( c=='0' ){ | |
| 201050 | + if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ | |
| 201051 | + assert( seenDP==JSON_INT ); | |
| 201052 | + pParse->hasNonstd = 1; | |
| 201053 | + jnFlags |= JNODE_JSON5; | |
| 201054 | + for(j=i+3; sqlite3Isxdigit(z[j]); j++){} | |
| 201055 | + goto parse_number_finish; | |
| 201056 | + }else if( sqlite3Isdigit(z[i+1]) ){ | |
| 201057 | + pParse->iErr = i+1; | |
| 201058 | + return -1; | |
| 201059 | + } | |
| 201060 | + }else{ | |
| 201061 | + if( !sqlite3Isdigit(z[i+1]) ){ | |
| 201062 | + /* JSON5 allows for "+Infinity" and "-Infinity" using exactly | |
| 201063 | + ** that case. SQLite also allows these in any case and it allows | |
| 201064 | + ** "+inf" and "-inf". */ | |
| 201065 | + if( (z[i+1]=='I' || z[i+1]=='i') | |
| 201066 | + && sqlite3StrNICmp(&z[i+1], "inf",3)==0 | |
| 201067 | + ){ | |
| 201068 | + pParse->hasNonstd = 1; | |
| 201069 | + if( z[i]=='-' ){ | |
| 201070 | + jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); | |
| 201071 | + }else{ | |
| 201072 | + jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); | |
| 201073 | + } | |
| 201074 | + return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); | |
| 201075 | + } | |
| 201076 | + if( z[i+1]=='.' ){ | |
| 201077 | + pParse->hasNonstd = 1; | |
| 201078 | + jnFlags |= JNODE_JSON5; | |
| 201079 | + goto parse_number_2; | |
| 201080 | + } | |
| 201081 | + pParse->iErr = i; | |
| 201082 | + return -1; | |
| 201083 | + } | |
| 201084 | + if( z[i+1]=='0' ){ | |
| 201085 | + if( sqlite3Isdigit(z[i+2]) ){ | |
| 201086 | + pParse->iErr = i+1; | |
| 201087 | + return -1; | |
| 201088 | + }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ | |
| 201089 | + pParse->hasNonstd = 1; | |
| 201090 | + jnFlags |= JNODE_JSON5; | |
| 201091 | + for(j=i+4; sqlite3Isxdigit(z[j]); j++){} | |
| 201092 | + goto parse_number_finish; | |
| 201093 | + } | |
| 201094 | + } | |
| 201095 | + } | |
| 200301 | 201096 | } |
| 200302 | - j = i+1; | |
| 200303 | - for(;; j++){ | |
| 201097 | + parse_number_2: | |
| 201098 | + for(j=i+1;; j++){ | |
| 200304 | 201099 | c = z[j]; |
| 200305 | - if( c>='0' && c<='9' ) continue; | |
| 201100 | + if( sqlite3Isdigit(c) ) continue; | |
| 200306 | 201101 | if( c=='.' ){ |
| 200307 | - if( z[j-1]=='-' ) return -1; | |
| 200308 | - if( seenDP ) return -1; | |
| 200309 | - seenDP = 1; | |
| 201102 | + if( seenDP==JSON_REAL ){ | |
| 201103 | + pParse->iErr = j; | |
| 201104 | + return -1; | |
| 201105 | + } | |
| 201106 | + seenDP = JSON_REAL; | |
| 200310 | 201107 | continue; |
| 200311 | 201108 | } |
| 200312 | 201109 | if( c=='e' || c=='E' ){ |
| 200313 | - if( z[j-1]<'0' ) return -1; | |
| 200314 | - if( seenE ) return -1; | |
| 200315 | - seenDP = seenE = 1; | |
| 201110 | + if( z[j-1]<'0' ){ | |
| 201111 | + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ | |
| 201112 | + pParse->hasNonstd = 1; | |
| 201113 | + jnFlags |= JNODE_JSON5; | |
| 201114 | + }else{ | |
| 201115 | + pParse->iErr = j; | |
| 201116 | + return -1; | |
| 201117 | + } | |
| 201118 | + } | |
| 201119 | + if( seenE ){ | |
| 201120 | + pParse->iErr = j; | |
| 201121 | + return -1; | |
| 201122 | + } | |
| 201123 | + seenDP = JSON_REAL; | |
| 201124 | + seenE = 1; | |
| 200316 | 201125 | c = z[j+1]; |
| 200317 | 201126 | if( c=='+' || c=='-' ){ |
| 200318 | 201127 | j++; |
| 200319 | 201128 | c = z[j+1]; |
| 200320 | 201129 | } |
| 200321 | - if( c<'0' || c>'9' ) return -1; | |
| 201130 | + if( c<'0' || c>'9' ){ | |
| 201131 | + pParse->iErr = j; | |
| 201132 | + return -1; | |
| 201133 | + } | |
| 200322 | 201134 | continue; |
| 200323 | 201135 | } |
| 200324 | -#ifdef SQLITE_ENABLE_JSON_NAN_INF | |
| 200325 | - /* Non-standard JSON: Allow "-Inf" (in any case) | |
| 200326 | - ** to be understood as floating point literals. */ | |
| 200327 | - if( (c=='i' || c=='I') | |
| 200328 | - && j==i+1 | |
| 200329 | - && z[i]=='-' | |
| 200330 | - && sqlite3StrNICmp(&z[j], "inf",3)==0 | |
| 200331 | - ){ | |
| 200332 | - if( !sqlite3Isalnum(z[j+3]) ){ | |
| 200333 | - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); | |
| 200334 | - return i+4; | |
| 200335 | - }else if( (sqlite3StrNICmp(&z[j],"infinity",8)==0 && | |
| 200336 | - !sqlite3Isalnum(z[j+8])) ){ | |
| 200337 | - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); | |
| 200338 | - return i+9; | |
| 200339 | - } | |
| 200340 | - } | |
| 200341 | -#endif | |
| 200342 | 201136 | break; |
| 200343 | 201137 | } |
| 200344 | - if( z[j-1]<'0' ) return -1; | |
| 200345 | - jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, | |
| 200346 | - j - i, &z[i]); | |
| 201138 | + if( z[j-1]<'0' ){ | |
| 201139 | + if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ | |
| 201140 | + pParse->hasNonstd = 1; | |
| 201141 | + jnFlags |= JNODE_JSON5; | |
| 201142 | + }else{ | |
| 201143 | + pParse->iErr = j; | |
| 201144 | + return -1; | |
| 201145 | + } | |
| 201146 | + } | |
| 201147 | + parse_number_finish: | |
| 201148 | + jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); | |
| 200347 | 201149 | return j; |
| 200348 | - }else if( c=='}' ){ | |
| 201150 | + } | |
| 201151 | + case '}': { | |
| 201152 | + pParse->iErr = i; | |
| 200349 | 201153 | return -2; /* End of {...} */ |
| 200350 | - }else if( c==']' ){ | |
| 201154 | + } | |
| 201155 | + case ']': { | |
| 201156 | + pParse->iErr = i; | |
| 200351 | 201157 | return -3; /* End of [...] */ |
| 200352 | - }else if( c==0 ){ | |
| 201158 | + } | |
| 201159 | + case ',': { | |
| 201160 | + pParse->iErr = i; | |
| 201161 | + return -4; /* List separator */ | |
| 201162 | + } | |
| 201163 | + case ':': { | |
| 201164 | + pParse->iErr = i; | |
| 201165 | + return -5; /* Object label/value separator */ | |
| 201166 | + } | |
| 201167 | + case 0: { | |
| 200353 | 201168 | return 0; /* End of file */ |
| 200354 | - }else{ | |
| 200355 | -#ifdef SQLITE_ENABLE_JSON_NAN_INF | |
| 200356 | - int k, nn; | |
| 201169 | + } | |
| 201170 | + case 0x09: | |
| 201171 | + case 0x0a: | |
| 201172 | + case 0x0d: | |
| 201173 | + case 0x20: { | |
| 201174 | + do{ | |
| 201175 | + i++; | |
| 201176 | + }while( fast_isspace(z[i]) ); | |
| 201177 | + goto json_parse_restart; | |
| 201178 | + } | |
| 201179 | + case 0x0b: | |
| 201180 | + case 0x0c: | |
| 201181 | + case '/': | |
| 201182 | + case 0xc2: | |
| 201183 | + case 0xe1: | |
| 201184 | + case 0xe2: | |
| 201185 | + case 0xe3: | |
| 201186 | + case 0xef: { | |
| 201187 | + j = json5Whitespace(&z[i]); | |
| 201188 | + if( j>0 ){ | |
| 201189 | + i += j; | |
| 201190 | + pParse->hasNonstd = 1; | |
| 201191 | + goto json_parse_restart; | |
| 201192 | + } | |
| 201193 | + pParse->iErr = i; | |
| 201194 | + return -1; | |
| 201195 | + } | |
| 201196 | + case 'n': { | |
| 201197 | + if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ | |
| 201198 | + jsonParseAddNode(pParse, JSON_NULL, 0, 0); | |
| 201199 | + return i+4; | |
| 201200 | + } | |
| 201201 | + /* fall-through into the default case that checks for NaN */ | |
| 201202 | + } | |
| 201203 | + default: { | |
| 201204 | + u32 k; | |
| 201205 | + int nn; | |
| 201206 | + c = z[i]; | |
| 200357 | 201207 | for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){ |
| 200358 | 201208 | if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue; |
| 200359 | 201209 | nn = aNanInfName[k].n; |
| 200360 | 201210 | if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){ |
| 200361 | 201211 | continue; |
| 200362 | 201212 | } |
| 200363 | 201213 | if( sqlite3Isalnum(z[i+nn]) ) continue; |
| 200364 | 201214 | jsonParseAddNode(pParse, aNanInfName[k].eType, |
| 200365 | 201215 | aNanInfName[k].nRepl, aNanInfName[k].zRepl); |
| 201216 | + pParse->hasNonstd = 1; | |
| 200366 | 201217 | return i + nn; |
| 200367 | 201218 | } |
| 200368 | -#endif | |
| 201219 | + pParse->iErr = i; | |
| 200369 | 201220 | return -1; /* Syntax error */ |
| 200370 | 201221 | } |
| 201222 | + } /* End switch(z[i]) */ | |
| 200371 | 201223 | } |
| 200372 | 201224 | |
| 200373 | 201225 | /* |
| 200374 | 201226 | ** Parse a complete JSON string. Return 0 on success or non-zero if there |
| 200375 | 201227 | ** are any errors. If an error occurs, free all memory associated with |
| @@ -200389,11 +201241,18 @@ | ||
| 200389 | 201241 | i = jsonParseValue(pParse, 0); |
| 200390 | 201242 | if( pParse->oom ) i = -1; |
| 200391 | 201243 | if( i>0 ){ |
| 200392 | 201244 | assert( pParse->iDepth==0 ); |
| 200393 | 201245 | while( fast_isspace(zJson[i]) ) i++; |
| 200394 | - if( zJson[i] ) i = -1; | |
| 201246 | + if( zJson[i] ){ | |
| 201247 | + i += json5Whitespace(&zJson[i]); | |
| 201248 | + if( zJson[i] ){ | |
| 201249 | + jsonParseReset(pParse); | |
| 201250 | + return 1; | |
| 201251 | + } | |
| 201252 | + pParse->hasNonstd = 1; | |
| 201253 | + } | |
| 200395 | 201254 | } |
| 200396 | 201255 | if( i<=0 ){ |
| 200397 | 201256 | if( pCtx!=0 ){ |
| 200398 | 201257 | if( pParse->oom ){ |
| 200399 | 201258 | sqlite3_result_error_nomem(pCtx); |
| @@ -200460,10 +201319,19 @@ | ||
| 200460 | 201319 | ** of the argv array. Use the sqlite3_get_auxdata() cache for this |
| 200461 | 201320 | ** parse if it is available. If the cache is not available or if it |
| 200462 | 201321 | ** is no longer valid, parse the JSON again and return the new parse, |
| 200463 | 201322 | ** and also register the new parse so that it will be available for |
| 200464 | 201323 | ** future sqlite3_get_auxdata() calls. |
| 201324 | +** | |
| 201325 | +** If an error occurs and pErrCtx!=0 then report the error on pErrCtx | |
| 201326 | +** and return NULL. | |
| 201327 | +** | |
| 201328 | +** If an error occurs and pErrCtx==0 then return the Parse object with | |
| 201329 | +** JsonParse.nErr non-zero. If the caller invokes this routine with | |
| 201330 | +** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller | |
| 201331 | +** is responsible for invoking jsonParseFree() on the returned value. | |
| 201332 | +** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. | |
| 200465 | 201333 | */ |
| 200466 | 201334 | static JsonParse *jsonParseCached( |
| 200467 | 201335 | sqlite3_context *pCtx, |
| 200468 | 201336 | sqlite3_value **argv, |
| 200469 | 201337 | sqlite3_context *pErrCtx |
| @@ -200509,10 +201377,14 @@ | ||
| 200509 | 201377 | } |
| 200510 | 201378 | memset(p, 0, sizeof(*p)); |
| 200511 | 201379 | p->zJson = (char*)&p[1]; |
| 200512 | 201380 | memcpy((char*)p->zJson, zJson, nJson+1); |
| 200513 | 201381 | if( jsonParse(p, pErrCtx, p->zJson) ){ |
| 201382 | + if( pErrCtx==0 ){ | |
| 201383 | + p->nErr = 1; | |
| 201384 | + return p; | |
| 201385 | + } | |
| 200514 | 201386 | sqlite3_free(p); |
| 200515 | 201387 | return 0; |
| 200516 | 201388 | } |
| 200517 | 201389 | p->nJson = nJson; |
| 200518 | 201390 | p->iHold = iMaxHold+1; |
| @@ -200523,19 +201395,28 @@ | ||
| 200523 | 201395 | |
| 200524 | 201396 | /* |
| 200525 | 201397 | ** Compare the OBJECT label at pNode against zKey,nKey. Return true on |
| 200526 | 201398 | ** a match. |
| 200527 | 201399 | */ |
| 200528 | -static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ | |
| 201400 | +static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ | |
| 200529 | 201401 | assert( pNode->eU==1 ); |
| 200530 | 201402 | if( pNode->jnFlags & JNODE_RAW ){ |
| 200531 | 201403 | if( pNode->n!=nKey ) return 0; |
| 200532 | 201404 | return strncmp(pNode->u.zJContent, zKey, nKey)==0; |
| 200533 | 201405 | }else{ |
| 200534 | 201406 | if( pNode->n!=nKey+2 ) return 0; |
| 200535 | 201407 | return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; |
| 200536 | 201408 | } |
| 201409 | +} | |
| 201410 | +static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ | |
| 201411 | + if( p1->jnFlags & JNODE_RAW ){ | |
| 201412 | + return jsonLabelCompare(p2, p1->u.zJContent, p1->n); | |
| 201413 | + }else if( p2->jnFlags & JNODE_RAW ){ | |
| 201414 | + return jsonLabelCompare(p1, p2->u.zJContent, p2->n); | |
| 201415 | + }else{ | |
| 201416 | + return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; | |
| 201417 | + } | |
| 200537 | 201418 | } |
| 200538 | 201419 | |
| 200539 | 201420 | /* forward declaration */ |
| 200540 | 201421 | static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); |
| 200541 | 201422 | |
| @@ -201003,11 +201884,11 @@ | ||
| 201003 | 201884 | if( argc==2 ){ |
| 201004 | 201885 | /* With a single PATH argument */ |
| 201005 | 201886 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 201006 | 201887 | if( zPath==0 ) return; |
| 201007 | 201888 | if( flags & JSON_ABPATH ){ |
| 201008 | - if( zPath[0]!='$' ){ | |
| 201889 | + if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){ | |
| 201009 | 201890 | /* The -> and ->> operators accept abbreviated PATH arguments. This |
| 201010 | 201891 | ** is mostly for compatibility with PostgreSQL, but also for |
| 201011 | 201892 | ** convenience. |
| 201012 | 201893 | ** |
| 201013 | 201894 | ** NUMBER ==> $[NUMBER] // PG compatible |
| @@ -201094,16 +201975,14 @@ | ||
| 201094 | 201975 | assert( pPatch[i].eType==JSON_STRING ); |
| 201095 | 201976 | assert( pPatch[i].jnFlags & JNODE_LABEL ); |
| 201096 | 201977 | assert( pPatch[i].eU==1 ); |
| 201097 | 201978 | nKey = pPatch[i].n; |
| 201098 | 201979 | zKey = pPatch[i].u.zJContent; |
| 201099 | - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); | |
| 201100 | 201980 | for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){ |
| 201101 | 201981 | assert( pTarget[j].eType==JSON_STRING ); |
| 201102 | 201982 | assert( pTarget[j].jnFlags & JNODE_LABEL ); |
| 201103 | - assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); | |
| 201104 | - if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ | |
| 201983 | + if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ | |
| 201105 | 201984 | if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; |
| 201106 | 201985 | if( pPatch[i+1].eType==JSON_NULL ){ |
| 201107 | 201986 | pTarget[j+1].jnFlags |= JNODE_REMOVE; |
| 201108 | 201987 | }else{ |
| 201109 | 201988 | JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); |
| @@ -201386,22 +202265,83 @@ | ||
| 201386 | 202265 | } |
| 201387 | 202266 | |
| 201388 | 202267 | /* |
| 201389 | 202268 | ** json_valid(JSON) |
| 201390 | 202269 | ** |
| 201391 | -** Return 1 if JSON is a well-formed JSON string according to RFC-7159. | |
| 201392 | -** Return 0 otherwise. | |
| 202270 | +** Return 1 if JSON is a well-formed canonical JSON string according | |
| 202271 | +** to RFC-7159. Return 0 otherwise. | |
| 201393 | 202272 | */ |
| 201394 | 202273 | static void jsonValidFunc( |
| 201395 | 202274 | sqlite3_context *ctx, |
| 201396 | 202275 | int argc, |
| 201397 | 202276 | sqlite3_value **argv |
| 201398 | 202277 | ){ |
| 201399 | 202278 | JsonParse *p; /* The parse */ |
| 201400 | 202279 | UNUSED_PARAMETER(argc); |
| 202280 | + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; | |
| 202281 | + p = jsonParseCached(ctx, argv, 0); | |
| 202282 | + if( p==0 || p->oom ){ | |
| 202283 | + sqlite3_result_error_nomem(ctx); | |
| 202284 | + sqlite3_free(p); | |
| 202285 | + }else{ | |
| 202286 | + sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); | |
| 202287 | + if( p->nErr ) jsonParseFree(p); | |
| 202288 | + } | |
| 202289 | +} | |
| 202290 | + | |
| 202291 | +/* | |
| 202292 | +** json_error_position(JSON) | |
| 202293 | +** | |
| 202294 | +** If the argument is not an interpretable JSON string, then return the 1-based | |
| 202295 | +** character position at which the parser first recognized that the input | |
| 202296 | +** was in error. The left-most character is 1. If the string is valid | |
| 202297 | +** JSON, then return 0. | |
| 202298 | +** | |
| 202299 | +** Note that json_valid() is only true for strictly conforming canonical JSON. | |
| 202300 | +** But this routine returns zero if the input contains extension. Thus: | |
| 202301 | +** | |
| 202302 | +** (1) If the input X is strictly conforming canonical JSON: | |
| 202303 | +** | |
| 202304 | +** json_valid(X) returns true | |
| 202305 | +** json_error_position(X) returns 0 | |
| 202306 | +** | |
| 202307 | +** (2) If the input X is JSON but it includes extension (such as JSON5) that | |
| 202308 | +** are not part of RFC-8259: | |
| 202309 | +** | |
| 202310 | +** json_valid(X) returns false | |
| 202311 | +** json_error_position(X) return 0 | |
| 202312 | +** | |
| 202313 | +** (3) If the input X cannot be interpreted as JSON even taking extensions | |
| 202314 | +** into account: | |
| 202315 | +** | |
| 202316 | +** json_valid(X) return false | |
| 202317 | +** json_error_position(X) returns 1 or more | |
| 202318 | +*/ | |
| 202319 | +static void jsonErrorFunc( | |
| 202320 | + sqlite3_context *ctx, | |
| 202321 | + int argc, | |
| 202322 | + sqlite3_value **argv | |
| 202323 | +){ | |
| 202324 | + JsonParse *p; /* The parse */ | |
| 202325 | + UNUSED_PARAMETER(argc); | |
| 202326 | + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; | |
| 201401 | 202327 | p = jsonParseCached(ctx, argv, 0); |
| 201402 | - sqlite3_result_int(ctx, p!=0); | |
| 202328 | + if( p==0 || p->oom ){ | |
| 202329 | + sqlite3_result_error_nomem(ctx); | |
| 202330 | + sqlite3_free(p); | |
| 202331 | + }else if( p->nErr==0 ){ | |
| 202332 | + sqlite3_result_int(ctx, 0); | |
| 202333 | + }else{ | |
| 202334 | + int n = 1; | |
| 202335 | + u32 i; | |
| 202336 | + const char *z = p->zJson; | |
| 202337 | + for(i=0; i<p->iErr && ALWAYS(z[i]); i++){ | |
| 202338 | + if( (z[i]&0xc0)!=0x80 ) n++; | |
| 202339 | + } | |
| 202340 | + sqlite3_result_int(ctx, n); | |
| 202341 | + jsonParseFree(p); | |
| 202342 | + } | |
| 201403 | 202343 | } |
| 201404 | 202344 | |
| 201405 | 202345 | |
| 201406 | 202346 | /**************************************************************************** |
| 201407 | 202347 | ** Aggregate SQL function implementations |
| @@ -201741,18 +202681,20 @@ | ||
| 201741 | 202681 | assert( pNode->eType==JSON_STRING ); |
| 201742 | 202682 | assert( pNode->jnFlags & JNODE_LABEL ); |
| 201743 | 202683 | assert( pNode->eU==1 ); |
| 201744 | 202684 | z = pNode->u.zJContent; |
| 201745 | 202685 | nn = pNode->n; |
| 201746 | - assert( nn>=2 ); | |
| 201747 | - assert( z[0]=='"' ); | |
| 201748 | - assert( z[nn-1]=='"' ); | |
| 201749 | - if( nn>2 && sqlite3Isalpha(z[1]) ){ | |
| 201750 | - for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){} | |
| 201751 | - if( jj==nn-1 ){ | |
| 201752 | - z++; | |
| 201753 | - nn -= 2; | |
| 202686 | + if( (pNode->jnFlags & JNODE_RAW)==0 ){ | |
| 202687 | + assert( nn>=2 ); | |
| 202688 | + assert( z[0]=='"' || z[0]=='\'' ); | |
| 202689 | + assert( z[nn-1]=='"' || z[0]=='\'' ); | |
| 202690 | + if( nn>2 && sqlite3Isalpha(z[1]) ){ | |
| 202691 | + for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){} | |
| 202692 | + if( jj==nn-1 ){ | |
| 202693 | + z++; | |
| 202694 | + nn -= 2; | |
| 202695 | + } | |
| 201754 | 202696 | } |
| 201755 | 202697 | } |
| 201756 | 202698 | jsonPrintf(nn+2, pStr, ".%.*s", nn, z); |
| 201757 | 202699 | } |
| 201758 | 202700 | |
| @@ -202108,10 +203050,11 @@ | ||
| 202108 | 203050 | static FuncDef aJsonFunc[] = { |
| 202109 | 203051 | JFUNCTION(json, 1, 0, jsonRemoveFunc), |
| 202110 | 203052 | JFUNCTION(json_array, -1, 0, jsonArrayFunc), |
| 202111 | 203053 | JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), |
| 202112 | 203054 | JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), |
| 203055 | + JFUNCTION(json_error_position,1, 0, jsonErrorFunc), | |
| 202113 | 203056 | JFUNCTION(json_extract, -1, 0, jsonExtractFunc), |
| 202114 | 203057 | JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), |
| 202115 | 203058 | JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), |
| 202116 | 203059 | JFUNCTION(json_insert, -1, 0, jsonSetFunc), |
| 202117 | 203060 | JFUNCTION(json_object, -1, 0, jsonObjectFunc), |
| @@ -202632,20 +203575,21 @@ | ||
| 202632 | 203575 | ** using C-preprocessor macros. If that is unsuccessful, or if |
| 202633 | 203576 | ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined |
| 202634 | 203577 | ** at run-time. |
| 202635 | 203578 | */ |
| 202636 | 203579 | #ifndef SQLITE_BYTEORDER |
| 202637 | -#if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ | |
| 202638 | - defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ | |
| 202639 | - defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ | |
| 202640 | - defined(__arm__) | |
| 202641 | -# define SQLITE_BYTEORDER 1234 | |
| 202642 | -#elif defined(sparc) || defined(__ppc__) | |
| 202643 | -# define SQLITE_BYTEORDER 4321 | |
| 202644 | -#else | |
| 202645 | -# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ | |
| 202646 | -#endif | |
| 203580 | +# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ | |
| 203581 | + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ | |
| 203582 | + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ | |
| 203583 | + defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) | |
| 203584 | +# define SQLITE_BYTEORDER 1234 | |
| 203585 | +# elif defined(sparc) || defined(__ppc__) || \ | |
| 203586 | + defined(__ARMEB__) || defined(__AARCH64EB__) | |
| 203587 | +# define SQLITE_BYTEORDER 4321 | |
| 203588 | +# else | |
| 203589 | +# define SQLITE_BYTEORDER 0 | |
| 203590 | +# endif | |
| 202647 | 203591 | #endif |
| 202648 | 203592 | |
| 202649 | 203593 | |
| 202650 | 203594 | /* What version of MSVC is being used. 0 means MSVC is not being used */ |
| 202651 | 203595 | #ifndef MSVC_VERSION |
| @@ -213201,15 +214145,22 @@ | ||
| 213201 | 214145 | static int rbuLockDatabase(sqlite3 *db){ |
| 213202 | 214146 | int rc = SQLITE_OK; |
| 213203 | 214147 | sqlite3_file *fd = 0; |
| 213204 | 214148 | |
| 213205 | 214149 | sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); |
| 213206 | - if( fd==0 ){ | |
| 214150 | + if( fd ){ | |
| 214151 | + sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); | |
| 214152 | + rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); | |
| 214153 | + if( rc==SQLITE_OK ){ | |
| 214154 | + rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE); | |
| 214155 | + } | |
| 214156 | + sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); | |
| 214157 | + }else{ | |
| 213207 | 214158 | sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); |
| 213208 | 214159 | } |
| 213209 | 214160 | |
| 213210 | - if( fd->pMethods ){ | |
| 214161 | + if( rc==SQLITE_OK && fd->pMethods ){ | |
| 213211 | 214162 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); |
| 213212 | 214163 | if( rc==SQLITE_OK ){ |
| 213213 | 214164 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); |
| 213214 | 214165 | } |
| 213215 | 214166 | } |
| @@ -216825,10 +217776,12 @@ | ||
| 216825 | 217776 | # define SESSIONS_STRM_CHUNK_SIZE 64 |
| 216826 | 217777 | # else |
| 216827 | 217778 | # define SESSIONS_STRM_CHUNK_SIZE 1024 |
| 216828 | 217779 | # endif |
| 216829 | 217780 | #endif |
| 217781 | + | |
| 217782 | +#define SESSIONS_ROWID "_rowid_" | |
| 216830 | 217783 | |
| 216831 | 217784 | static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; |
| 216832 | 217785 | |
| 216833 | 217786 | typedef struct SessionHook SessionHook; |
| 216834 | 217787 | struct SessionHook { |
| @@ -216847,10 +217800,11 @@ | ||
| 216847 | 217800 | char *zDb; /* Name of database session is attached to */ |
| 216848 | 217801 | int bEnableSize; /* True if changeset_size() enabled */ |
| 216849 | 217802 | int bEnable; /* True if currently recording */ |
| 216850 | 217803 | int bIndirect; /* True if all changes are indirect */ |
| 216851 | 217804 | int bAutoAttach; /* True to auto-attach tables */ |
| 217805 | + int bImplicitPK; /* True to handle tables with implicit PK */ | |
| 216852 | 217806 | int rc; /* Non-zero if an error has occurred */ |
| 216853 | 217807 | void *pFilterCtx; /* First argument to pass to xTableFilter */ |
| 216854 | 217808 | int (*xTableFilter)(void *pCtx, const char *zTab); |
| 216855 | 217809 | i64 nMalloc; /* Number of bytes of data allocated */ |
| 216856 | 217810 | i64 nMaxChangesetSize; |
| @@ -216923,10 +217877,11 @@ | ||
| 216923 | 217877 | struct SessionTable { |
| 216924 | 217878 | SessionTable *pNext; |
| 216925 | 217879 | char *zName; /* Local name of table */ |
| 216926 | 217880 | int nCol; /* Number of columns in table zName */ |
| 216927 | 217881 | int bStat1; /* True if this is sqlite_stat1 */ |
| 217882 | + int bRowid; /* True if this table uses rowid for PK */ | |
| 216928 | 217883 | const char **azCol; /* Column names */ |
| 216929 | 217884 | u8 *abPK; /* Array of primary key flags */ |
| 216930 | 217885 | int nEntry; /* Total number of entries in hash table */ |
| 216931 | 217886 | int nChange; /* Size of apChange[] array */ |
| 216932 | 217887 | SessionChange **apChange; /* Hash table buckets */ |
| @@ -217315,60 +218270,66 @@ | ||
| 217315 | 218270 | ** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned |
| 217316 | 218271 | ** and the output variables are set as described above. |
| 217317 | 218272 | */ |
| 217318 | 218273 | static int sessionPreupdateHash( |
| 217319 | 218274 | sqlite3_session *pSession, /* Session object that owns pTab */ |
| 218275 | + i64 iRowid, | |
| 217320 | 218276 | SessionTable *pTab, /* Session table handle */ |
| 217321 | 218277 | int bNew, /* True to hash the new.* PK */ |
| 217322 | 218278 | int *piHash, /* OUT: Hash value */ |
| 217323 | 218279 | int *pbNullPK /* OUT: True if there are NULL values in PK */ |
| 217324 | 218280 | ){ |
| 217325 | 218281 | unsigned int h = 0; /* Hash value to return */ |
| 217326 | 218282 | int i; /* Used to iterate through columns */ |
| 217327 | 218283 | |
| 217328 | - assert( *pbNullPK==0 ); | |
| 217329 | - assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); | |
| 217330 | - for(i=0; i<pTab->nCol; i++){ | |
| 217331 | - if( pTab->abPK[i] ){ | |
| 217332 | - int rc; | |
| 217333 | - int eType; | |
| 217334 | - sqlite3_value *pVal; | |
| 217335 | - | |
| 217336 | - if( bNew ){ | |
| 217337 | - rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); | |
| 217338 | - }else{ | |
| 217339 | - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); | |
| 217340 | - } | |
| 217341 | - if( rc!=SQLITE_OK ) return rc; | |
| 217342 | - | |
| 217343 | - eType = sqlite3_value_type(pVal); | |
| 217344 | - h = sessionHashAppendType(h, eType); | |
| 217345 | - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ | |
| 217346 | - i64 iVal; | |
| 217347 | - if( eType==SQLITE_INTEGER ){ | |
| 217348 | - iVal = sqlite3_value_int64(pVal); | |
| 217349 | - }else{ | |
| 217350 | - double rVal = sqlite3_value_double(pVal); | |
| 217351 | - assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); | |
| 217352 | - memcpy(&iVal, &rVal, 8); | |
| 217353 | - } | |
| 217354 | - h = sessionHashAppendI64(h, iVal); | |
| 217355 | - }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ | |
| 217356 | - const u8 *z; | |
| 217357 | - int n; | |
| 217358 | - if( eType==SQLITE_TEXT ){ | |
| 217359 | - z = (const u8 *)sqlite3_value_text(pVal); | |
| 217360 | - }else{ | |
| 217361 | - z = (const u8 *)sqlite3_value_blob(pVal); | |
| 217362 | - } | |
| 217363 | - n = sqlite3_value_bytes(pVal); | |
| 217364 | - if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; | |
| 217365 | - h = sessionHashAppendBlob(h, n, z); | |
| 217366 | - }else{ | |
| 217367 | - assert( eType==SQLITE_NULL ); | |
| 217368 | - assert( pTab->bStat1==0 || i!=1 ); | |
| 217369 | - *pbNullPK = 1; | |
| 218284 | + if( pTab->bRowid ){ | |
| 218285 | + assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) ); | |
| 218286 | + h = sessionHashAppendI64(h, iRowid); | |
| 218287 | + }else{ | |
| 218288 | + assert( *pbNullPK==0 ); | |
| 218289 | + assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); | |
| 218290 | + for(i=0; i<pTab->nCol; i++){ | |
| 218291 | + if( pTab->abPK[i] ){ | |
| 218292 | + int rc; | |
| 218293 | + int eType; | |
| 218294 | + sqlite3_value *pVal; | |
| 218295 | + | |
| 218296 | + if( bNew ){ | |
| 218297 | + rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); | |
| 218298 | + }else{ | |
| 218299 | + rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); | |
| 218300 | + } | |
| 218301 | + if( rc!=SQLITE_OK ) return rc; | |
| 218302 | + | |
| 218303 | + eType = sqlite3_value_type(pVal); | |
| 218304 | + h = sessionHashAppendType(h, eType); | |
| 218305 | + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ | |
| 218306 | + i64 iVal; | |
| 218307 | + if( eType==SQLITE_INTEGER ){ | |
| 218308 | + iVal = sqlite3_value_int64(pVal); | |
| 218309 | + }else{ | |
| 218310 | + double rVal = sqlite3_value_double(pVal); | |
| 218311 | + assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); | |
| 218312 | + memcpy(&iVal, &rVal, 8); | |
| 218313 | + } | |
| 218314 | + h = sessionHashAppendI64(h, iVal); | |
| 218315 | + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ | |
| 218316 | + const u8 *z; | |
| 218317 | + int n; | |
| 218318 | + if( eType==SQLITE_TEXT ){ | |
| 218319 | + z = (const u8 *)sqlite3_value_text(pVal); | |
| 218320 | + }else{ | |
| 218321 | + z = (const u8 *)sqlite3_value_blob(pVal); | |
| 218322 | + } | |
| 218323 | + n = sqlite3_value_bytes(pVal); | |
| 218324 | + if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; | |
| 218325 | + h = sessionHashAppendBlob(h, n, z); | |
| 218326 | + }else{ | |
| 218327 | + assert( eType==SQLITE_NULL ); | |
| 218328 | + assert( pTab->bStat1==0 || i!=1 ); | |
| 218329 | + *pbNullPK = 1; | |
| 218330 | + } | |
| 217370 | 218331 | } |
| 217371 | 218332 | } |
| 217372 | 218333 | } |
| 217373 | 218334 | |
| 217374 | 218335 | *piHash = (h % pTab->nChange); |
| @@ -217647,16 +218608,22 @@ | ||
| 217647 | 218608 | ** if the pre-update-hook does not affect the same row as pChange, it returns |
| 217648 | 218609 | ** false. |
| 217649 | 218610 | */ |
| 217650 | 218611 | static int sessionPreupdateEqual( |
| 217651 | 218612 | sqlite3_session *pSession, /* Session object that owns SessionTable */ |
| 218613 | + i64 iRowid, /* Rowid value if pTab->bRowid */ | |
| 217652 | 218614 | SessionTable *pTab, /* Table associated with change */ |
| 217653 | 218615 | SessionChange *pChange, /* Change to compare to */ |
| 217654 | 218616 | int op /* Current pre-update operation */ |
| 217655 | 218617 | ){ |
| 217656 | 218618 | int iCol; /* Used to iterate through columns */ |
| 217657 | 218619 | u8 *a = pChange->aRecord; /* Cursor used to scan change record */ |
| 218620 | + | |
| 218621 | + if( pTab->bRowid ){ | |
| 218622 | + if( a[0]!=SQLITE_INTEGER ) return 0; | |
| 218623 | + return sessionGetI64(&a[1])==iRowid; | |
| 218624 | + } | |
| 217658 | 218625 | |
| 217659 | 218626 | assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); |
| 217660 | 218627 | for(iCol=0; iCol<pTab->nCol; iCol++){ |
| 217661 | 218628 | if( !pTab->abPK[iCol] ){ |
| 217662 | 218629 | a += sessionSerialLen(a); |
| @@ -217798,11 +218765,12 @@ | ||
| 217798 | 218765 | const char *zDb, /* Name of attached database (e.g. "main") */ |
| 217799 | 218766 | const char *zThis, /* Table name */ |
| 217800 | 218767 | int *pnCol, /* OUT: number of columns */ |
| 217801 | 218768 | const char **pzTab, /* OUT: Copy of zThis */ |
| 217802 | 218769 | const char ***pazCol, /* OUT: Array of column names for table */ |
| 217803 | - u8 **pabPK /* OUT: Array of booleans - true for PK col */ | |
| 218770 | + u8 **pabPK, /* OUT: Array of booleans - true for PK col */ | |
| 218771 | + int *pbRowid /* OUT: True if only PK is a rowid */ | |
| 217804 | 218772 | ){ |
| 217805 | 218773 | char *zPragma; |
| 217806 | 218774 | sqlite3_stmt *pStmt; |
| 217807 | 218775 | int rc; |
| 217808 | 218776 | sqlite3_int64 nByte; |
| @@ -217810,10 +218778,11 @@ | ||
| 217810 | 218778 | int nThis; |
| 217811 | 218779 | int i; |
| 217812 | 218780 | u8 *pAlloc = 0; |
| 217813 | 218781 | char **azCol = 0; |
| 217814 | 218782 | u8 *abPK = 0; |
| 218783 | + int bRowid = 0; /* Set to true to use rowid as PK */ | |
| 217815 | 218784 | |
| 217816 | 218785 | assert( pazCol && pabPK ); |
| 217817 | 218786 | |
| 217818 | 218787 | nThis = sqlite3Strlen30(zThis); |
| 217819 | 218788 | if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ |
| @@ -217854,14 +218823,19 @@ | ||
| 217854 | 218823 | if( pzTab ) *pzTab = 0; |
| 217855 | 218824 | return rc; |
| 217856 | 218825 | } |
| 217857 | 218826 | |
| 217858 | 218827 | nByte = nThis + 1; |
| 218828 | + bRowid = (pbRowid!=0); | |
| 217859 | 218829 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 217860 | 218830 | nByte += sqlite3_column_bytes(pStmt, 1); |
| 217861 | 218831 | nDbCol++; |
| 218832 | + if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; | |
| 217862 | 218833 | } |
| 218834 | + if( nDbCol==0 ) bRowid = 0; | |
| 218835 | + nDbCol += bRowid; | |
| 218836 | + nByte += strlen(SESSIONS_ROWID); | |
| 217863 | 218837 | rc = sqlite3_reset(pStmt); |
| 217864 | 218838 | |
| 217865 | 218839 | if( rc==SQLITE_OK ){ |
| 217866 | 218840 | nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); |
| 217867 | 218841 | pAlloc = sessionMalloc64(pSession, nByte); |
| @@ -217879,10 +218853,18 @@ | ||
| 217879 | 218853 | *pzTab = (char *)pAlloc; |
| 217880 | 218854 | pAlloc += nThis+1; |
| 217881 | 218855 | } |
| 217882 | 218856 | |
| 217883 | 218857 | i = 0; |
| 218858 | + if( bRowid ){ | |
| 218859 | + size_t nName = strlen(SESSIONS_ROWID); | |
| 218860 | + memcpy(pAlloc, SESSIONS_ROWID, nName+1); | |
| 218861 | + azCol[i] = (char*)pAlloc; | |
| 218862 | + pAlloc += nName+1; | |
| 218863 | + abPK[i] = 1; | |
| 218864 | + i++; | |
| 218865 | + } | |
| 217884 | 218866 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 217885 | 218867 | int nName = sqlite3_column_bytes(pStmt, 1); |
| 217886 | 218868 | const unsigned char *zName = sqlite3_column_text(pStmt, 1); |
| 217887 | 218869 | if( zName==0 ) break; |
| 217888 | 218870 | memcpy(pAlloc, zName, nName+1); |
| @@ -217890,11 +218872,10 @@ | ||
| 217890 | 218872 | pAlloc += nName+1; |
| 217891 | 218873 | abPK[i] = sqlite3_column_int(pStmt, 5); |
| 217892 | 218874 | i++; |
| 217893 | 218875 | } |
| 217894 | 218876 | rc = sqlite3_reset(pStmt); |
| 217895 | - | |
| 217896 | 218877 | } |
| 217897 | 218878 | |
| 217898 | 218879 | /* If successful, populate the output variables. Otherwise, zero them and |
| 217899 | 218880 | ** free any allocation made. An error code will be returned in this case. |
| 217900 | 218881 | */ |
| @@ -217907,10 +218888,11 @@ | ||
| 217907 | 218888 | *pabPK = 0; |
| 217908 | 218889 | *pnCol = 0; |
| 217909 | 218890 | if( pzTab ) *pzTab = 0; |
| 217910 | 218891 | sessionFree(pSession, azCol); |
| 217911 | 218892 | } |
| 218893 | + if( pbRowid ) *pbRowid = bRowid; | |
| 217912 | 218894 | sqlite3_finalize(pStmt); |
| 217913 | 218895 | return rc; |
| 217914 | 218896 | } |
| 217915 | 218897 | |
| 217916 | 218898 | /* |
| @@ -217928,11 +218910,12 @@ | ||
| 217928 | 218910 | static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ |
| 217929 | 218911 | if( pTab->nCol==0 ){ |
| 217930 | 218912 | u8 *abPK; |
| 217931 | 218913 | assert( pTab->azCol==0 || pTab->abPK==0 ); |
| 217932 | 218914 | pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, |
| 217933 | - pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK | |
| 218915 | + pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK, | |
| 218916 | + (pSession->bImplicitPK ? &pTab->bRowid : 0) | |
| 217934 | 218917 | ); |
| 217935 | 218918 | if( pSession->rc==SQLITE_OK ){ |
| 217936 | 218919 | int i; |
| 217937 | 218920 | for(i=0; i<pTab->nCol; i++){ |
| 217938 | 218921 | if( abPK[i] ){ |
| @@ -218000,10 +218983,11 @@ | ||
| 218000 | 218983 | SessionTable *pTab, /* Table that change applies to */ |
| 218001 | 218984 | SessionChange *pC /* Update pC->nMaxSize */ |
| 218002 | 218985 | ){ |
| 218003 | 218986 | i64 nNew = 2; |
| 218004 | 218987 | if( pC->op==SQLITE_INSERT ){ |
| 218988 | + if( pTab->bRowid ) nNew += 9; | |
| 218005 | 218989 | if( op!=SQLITE_DELETE ){ |
| 218006 | 218990 | int ii; |
| 218007 | 218991 | for(ii=0; ii<pTab->nCol; ii++){ |
| 218008 | 218992 | sqlite3_value *p = 0; |
| 218009 | 218993 | pSession->hook.xNew(pSession->hook.pCtx, ii, &p); |
| @@ -218016,16 +219000,20 @@ | ||
| 218016 | 219000 | nNew += pC->nRecord; |
| 218017 | 219001 | } |
| 218018 | 219002 | }else{ |
| 218019 | 219003 | int ii; |
| 218020 | 219004 | u8 *pCsr = pC->aRecord; |
| 218021 | - for(ii=0; ii<pTab->nCol; ii++){ | |
| 219005 | + if( pTab->bRowid ){ | |
| 219006 | + nNew += 9 + 1; | |
| 219007 | + pCsr += 9; | |
| 219008 | + } | |
| 219009 | + for(ii=pTab->bRowid; ii<pTab->nCol; ii++){ | |
| 218022 | 219010 | int bChanged = 1; |
| 218023 | 219011 | int nOld = 0; |
| 218024 | 219012 | int eType; |
| 218025 | 219013 | sqlite3_value *p = 0; |
| 218026 | - pSession->hook.xNew(pSession->hook.pCtx, ii, &p); | |
| 219014 | + pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p); | |
| 218027 | 219015 | if( p==0 ){ |
| 218028 | 219016 | return SQLITE_NOMEM; |
| 218029 | 219017 | } |
| 218030 | 219018 | |
| 218031 | 219019 | eType = *pCsr++; |
| @@ -218100,10 +219088,11 @@ | ||
| 218100 | 219088 | ** Unless one is already present or an error occurs, an entry is added |
| 218101 | 219089 | ** to the changed-rows hash table associated with table pTab. |
| 218102 | 219090 | */ |
| 218103 | 219091 | static void sessionPreupdateOneChange( |
| 218104 | 219092 | int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ |
| 219093 | + i64 iRowid, | |
| 218105 | 219094 | sqlite3_session *pSession, /* Session object pTab is attached to */ |
| 218106 | 219095 | SessionTable *pTab /* Table that change applies to */ |
| 218107 | 219096 | ){ |
| 218108 | 219097 | int iHash; |
| 218109 | 219098 | int bNull = 0; |
| @@ -218115,11 +219104,11 @@ | ||
| 218115 | 219104 | /* Load table details if required */ |
| 218116 | 219105 | if( sessionInitTable(pSession, pTab) ) return; |
| 218117 | 219106 | |
| 218118 | 219107 | /* Check the number of columns in this xPreUpdate call matches the |
| 218119 | 219108 | ** number of columns in the table. */ |
| 218120 | - if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){ | |
| 219109 | + if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){ | |
| 218121 | 219110 | pSession->rc = SQLITE_SCHEMA; |
| 218122 | 219111 | return; |
| 218123 | 219112 | } |
| 218124 | 219113 | |
| 218125 | 219114 | /* Grow the hash table if required */ |
| @@ -218148,18 +219137,20 @@ | ||
| 218148 | 219137 | } |
| 218149 | 219138 | |
| 218150 | 219139 | /* Calculate the hash-key for this change. If the primary key of the row |
| 218151 | 219140 | ** includes a NULL value, exit early. Such changes are ignored by the |
| 218152 | 219141 | ** session module. */ |
| 218153 | - rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); | |
| 219142 | + rc = sessionPreupdateHash( | |
| 219143 | + pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull | |
| 219144 | + ); | |
| 218154 | 219145 | if( rc!=SQLITE_OK ) goto error_out; |
| 218155 | 219146 | |
| 218156 | 219147 | if( bNull==0 ){ |
| 218157 | 219148 | /* Search the hash table for an existing record for this row. */ |
| 218158 | 219149 | SessionChange *pC; |
| 218159 | 219150 | for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ |
| 218160 | - if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break; | |
| 219151 | + if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break; | |
| 218161 | 219152 | } |
| 218162 | 219153 | |
| 218163 | 219154 | if( pC==0 ){ |
| 218164 | 219155 | /* Create a new change object containing all the old values (if |
| 218165 | 219156 | ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK |
| @@ -218170,11 +219161,11 @@ | ||
| 218170 | 219161 | assert( rc==SQLITE_OK ); |
| 218171 | 219162 | pTab->nEntry++; |
| 218172 | 219163 | |
| 218173 | 219164 | /* Figure out how large an allocation is required */ |
| 218174 | 219165 | nByte = sizeof(SessionChange); |
| 218175 | - for(i=0; i<pTab->nCol; i++){ | |
| 219166 | + for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ | |
| 218176 | 219167 | sqlite3_value *p = 0; |
| 218177 | 219168 | if( op!=SQLITE_INSERT ){ |
| 218178 | 219169 | TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 218179 | 219170 | assert( trc==SQLITE_OK ); |
| 218180 | 219171 | }else if( pTab->abPK[i] ){ |
| @@ -218185,10 +219176,13 @@ | ||
| 218185 | 219176 | /* This may fail if SQLite value p contains a utf-16 string that must |
| 218186 | 219177 | ** be converted to utf-8 and an OOM error occurs while doing so. */ |
| 218187 | 219178 | rc = sessionSerializeValue(0, p, &nByte); |
| 218188 | 219179 | if( rc!=SQLITE_OK ) goto error_out; |
| 218189 | 219180 | } |
| 219181 | + if( pTab->bRowid ){ | |
| 219182 | + nByte += 9; /* Size of rowid field - an integer */ | |
| 219183 | + } | |
| 218190 | 219184 | |
| 218191 | 219185 | /* Allocate the change object */ |
| 218192 | 219186 | pC = (SessionChange *)sessionMalloc64(pSession, nByte); |
| 218193 | 219187 | if( !pC ){ |
| 218194 | 219188 | rc = SQLITE_NOMEM; |
| @@ -218201,11 +219195,16 @@ | ||
| 218201 | 219195 | /* Populate the change object. None of the preupdate_old(), |
| 218202 | 219196 | ** preupdate_new() or SerializeValue() calls below may fail as all |
| 218203 | 219197 | ** required values and encodings have already been cached in memory. |
| 218204 | 219198 | ** It is not possible for an OOM to occur in this block. */ |
| 218205 | 219199 | nByte = 0; |
| 218206 | - for(i=0; i<pTab->nCol; i++){ | |
| 219200 | + if( pTab->bRowid ){ | |
| 219201 | + pC->aRecord[0] = SQLITE_INTEGER; | |
| 219202 | + sessionPutI64(&pC->aRecord[1], iRowid); | |
| 219203 | + nByte = 9; | |
| 219204 | + } | |
| 219205 | + for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ | |
| 218207 | 219206 | sqlite3_value *p = 0; |
| 218208 | 219207 | if( op!=SQLITE_INSERT ){ |
| 218209 | 219208 | pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 218210 | 219209 | }else if( pTab->abPK[i] ){ |
| 218211 | 219210 | pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| @@ -218316,13 +219315,14 @@ | ||
| 218316 | 219315 | if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue; |
| 218317 | 219316 | |
| 218318 | 219317 | pSession->rc = sessionFindTable(pSession, zName, &pTab); |
| 218319 | 219318 | if( pTab ){ |
| 218320 | 219319 | assert( pSession->rc==SQLITE_OK ); |
| 218321 | - sessionPreupdateOneChange(op, pSession, pTab); | |
| 219320 | + assert( op==SQLITE_UPDATE || iKey1==iKey2 ); | |
| 219321 | + sessionPreupdateOneChange(op, iKey1, pSession, pTab); | |
| 218322 | 219322 | if( op==SQLITE_UPDATE ){ |
| 218323 | - sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); | |
| 219323 | + sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab); | |
| 218324 | 219324 | } |
| 218325 | 219325 | } |
| 218326 | 219326 | } |
| 218327 | 219327 | } |
| 218328 | 219328 | |
| @@ -218357,29 +219357,30 @@ | ||
| 218357 | 219357 | } |
| 218358 | 219358 | |
| 218359 | 219359 | typedef struct SessionDiffCtx SessionDiffCtx; |
| 218360 | 219360 | struct SessionDiffCtx { |
| 218361 | 219361 | sqlite3_stmt *pStmt; |
| 219362 | + int bRowid; | |
| 218362 | 219363 | int nOldOff; |
| 218363 | 219364 | }; |
| 218364 | 219365 | |
| 218365 | 219366 | /* |
| 218366 | 219367 | ** The diff hook implementations. |
| 218367 | 219368 | */ |
| 218368 | 219369 | static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 218369 | 219370 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218370 | - *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff); | |
| 219371 | + *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid); | |
| 218371 | 219372 | return SQLITE_OK; |
| 218372 | 219373 | } |
| 218373 | 219374 | static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 218374 | 219375 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218375 | - *ppVal = sqlite3_column_value(p->pStmt, iVal); | |
| 219376 | + *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid); | |
| 218376 | 219377 | return SQLITE_OK; |
| 218377 | 219378 | } |
| 218378 | 219379 | static int sessionDiffCount(void *pCtx){ |
| 218379 | 219380 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218380 | - return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt); | |
| 219381 | + return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid; | |
| 218381 | 219382 | } |
| 218382 | 219383 | static int sessionDiffDepth(void *pCtx){ |
| 218383 | 219384 | (void)pCtx; |
| 218384 | 219385 | return 0; |
| 218385 | 219386 | } |
| @@ -218454,18 +219455,20 @@ | ||
| 218454 | 219455 | } |
| 218455 | 219456 | |
| 218456 | 219457 | static char *sessionSelectFindNew( |
| 218457 | 219458 | const char *zDb1, /* Pick rows in this db only */ |
| 218458 | 219459 | const char *zDb2, /* But not in this one */ |
| 219460 | + int bRowid, | |
| 218459 | 219461 | const char *zTbl, /* Table name */ |
| 218460 | 219462 | const char *zExpr |
| 218461 | 219463 | ){ |
| 219464 | + const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*"); | |
| 218462 | 219465 | char *zRet = sqlite3_mprintf( |
| 218463 | - "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS (" | |
| 219466 | + "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS (" | |
| 218464 | 219467 | " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" |
| 218465 | 219468 | ")", |
| 218466 | - zDb1, zTbl, zDb2, zTbl, zExpr | |
| 219469 | + zSel, zDb1, zTbl, zDb2, zTbl, zExpr | |
| 218467 | 219470 | ); |
| 218468 | 219471 | return zRet; |
| 218469 | 219472 | } |
| 218470 | 219473 | |
| 218471 | 219474 | static int sessionDiffFindNew( |
| @@ -218475,11 +219478,13 @@ | ||
| 218475 | 219478 | const char *zDb1, |
| 218476 | 219479 | const char *zDb2, |
| 218477 | 219480 | char *zExpr |
| 218478 | 219481 | ){ |
| 218479 | 219482 | int rc = SQLITE_OK; |
| 218480 | - char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr); | |
| 219483 | + char *zStmt = sessionSelectFindNew( | |
| 219484 | + zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr | |
| 219485 | + ); | |
| 218481 | 219486 | |
| 218482 | 219487 | if( zStmt==0 ){ |
| 218483 | 219488 | rc = SQLITE_NOMEM; |
| 218484 | 219489 | }else{ |
| 218485 | 219490 | sqlite3_stmt *pStmt; |
| @@ -218486,20 +219491,43 @@ | ||
| 218486 | 219491 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 218487 | 219492 | if( rc==SQLITE_OK ){ |
| 218488 | 219493 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 218489 | 219494 | pDiffCtx->pStmt = pStmt; |
| 218490 | 219495 | pDiffCtx->nOldOff = 0; |
| 219496 | + pDiffCtx->bRowid = pTab->bRowid; | |
| 218491 | 219497 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218492 | - sessionPreupdateOneChange(op, pSession, pTab); | |
| 219498 | + i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); | |
| 219499 | + sessionPreupdateOneChange(op, iRowid, pSession, pTab); | |
| 218493 | 219500 | } |
| 218494 | 219501 | rc = sqlite3_finalize(pStmt); |
| 218495 | 219502 | } |
| 218496 | 219503 | sqlite3_free(zStmt); |
| 218497 | 219504 | } |
| 218498 | 219505 | |
| 218499 | 219506 | return rc; |
| 218500 | 219507 | } |
| 219508 | + | |
| 219509 | +/* | |
| 219510 | +** Return a comma-separated list of the fully-qualified (with both database | |
| 219511 | +** and table name) column names from table pTab. e.g. | |
| 219512 | +** | |
| 219513 | +** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c" | |
| 219514 | +*/ | |
| 219515 | +static char *sessionAllCols( | |
| 219516 | + const char *zDb, | |
| 219517 | + SessionTable *pTab | |
| 219518 | +){ | |
| 219519 | + int ii; | |
| 219520 | + char *zRet = 0; | |
| 219521 | + for(ii=0; ii<pTab->nCol; ii++){ | |
| 219522 | + zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"", | |
| 219523 | + zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] | |
| 219524 | + ); | |
| 219525 | + if( !zRet ) break; | |
| 219526 | + } | |
| 219527 | + return zRet; | |
| 219528 | +} | |
| 218501 | 219529 | |
| 218502 | 219530 | static int sessionDiffFindModified( |
| 218503 | 219531 | sqlite3_session *pSession, |
| 218504 | 219532 | SessionTable *pTab, |
| 218505 | 219533 | const char *zFrom, |
| @@ -218511,15 +219539,17 @@ | ||
| 218511 | 219539 | pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK |
| 218512 | 219540 | ); |
| 218513 | 219541 | if( zExpr2==0 ){ |
| 218514 | 219542 | rc = SQLITE_NOMEM; |
| 218515 | 219543 | }else{ |
| 219544 | + char *z1 = sessionAllCols(pSession->zDb, pTab); | |
| 219545 | + char *z2 = sessionAllCols(zFrom, pTab); | |
| 218516 | 219546 | char *zStmt = sqlite3_mprintf( |
| 218517 | - "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", | |
| 218518 | - pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 | |
| 219547 | + "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", | |
| 219548 | + z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 | |
| 218519 | 219549 | ); |
| 218520 | - if( zStmt==0 ){ | |
| 219550 | + if( zStmt==0 || z1==0 || z2==0 ){ | |
| 218521 | 219551 | rc = SQLITE_NOMEM; |
| 218522 | 219552 | }else{ |
| 218523 | 219553 | sqlite3_stmt *pStmt; |
| 218524 | 219554 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 218525 | 219555 | |
| @@ -218526,16 +219556,19 @@ | ||
| 218526 | 219556 | if( rc==SQLITE_OK ){ |
| 218527 | 219557 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 218528 | 219558 | pDiffCtx->pStmt = pStmt; |
| 218529 | 219559 | pDiffCtx->nOldOff = pTab->nCol; |
| 218530 | 219560 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218531 | - sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab); | |
| 219561 | + i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); | |
| 219562 | + sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab); | |
| 218532 | 219563 | } |
| 218533 | 219564 | rc = sqlite3_finalize(pStmt); |
| 218534 | 219565 | } |
| 218535 | - sqlite3_free(zStmt); | |
| 218536 | 219566 | } |
| 219567 | + sqlite3_free(zStmt); | |
| 219568 | + sqlite3_free(z1); | |
| 219569 | + sqlite3_free(z2); | |
| 218537 | 219570 | } |
| 218538 | 219571 | |
| 218539 | 219572 | return rc; |
| 218540 | 219573 | } |
| 218541 | 219574 | |
| @@ -218570,13 +219603,16 @@ | ||
| 218570 | 219603 | /* Check the table schemas match */ |
| 218571 | 219604 | if( rc==SQLITE_OK ){ |
| 218572 | 219605 | int bHasPk = 0; |
| 218573 | 219606 | int bMismatch = 0; |
| 218574 | 219607 | int nCol; /* Columns in zFrom.zTbl */ |
| 219608 | + int bRowid = 0; | |
| 218575 | 219609 | u8 *abPK; |
| 218576 | 219610 | const char **azCol = 0; |
| 218577 | - rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); | |
| 219611 | + rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK, | |
| 219612 | + pSession->bImplicitPK ? &bRowid : 0 | |
| 219613 | + ); | |
| 218578 | 219614 | if( rc==SQLITE_OK ){ |
| 218579 | 219615 | if( pTo->nCol!=nCol ){ |
| 218580 | 219616 | bMismatch = 1; |
| 218581 | 219617 | }else{ |
| 218582 | 219618 | int i; |
| @@ -219121,11 +220157,11 @@ | ||
| 219121 | 220157 | } |
| 219122 | 220158 | |
| 219123 | 220159 | /* If at least one field has been modified, this is not a no-op. */ |
| 219124 | 220160 | if( bChanged ) bNoop = 0; |
| 219125 | 220161 | |
| 219126 | - /* Add a field to the old.* record. This is omitted if this modules is | |
| 220162 | + /* Add a field to the old.* record. This is omitted if this module is | |
| 219127 | 220163 | ** currently generating a patchset. */ |
| 219128 | 220164 | if( bPatchset==0 ){ |
| 219129 | 220165 | if( bChanged || abPK[i] ){ |
| 219130 | 220166 | sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); |
| 219131 | 220167 | }else{ |
| @@ -219223,19 +220259,20 @@ | ||
| 219223 | 220259 | static int sessionSelectStmt( |
| 219224 | 220260 | sqlite3 *db, /* Database handle */ |
| 219225 | 220261 | int bIgnoreNoop, |
| 219226 | 220262 | const char *zDb, /* Database name */ |
| 219227 | 220263 | const char *zTab, /* Table name */ |
| 220264 | + int bRowid, | |
| 219228 | 220265 | int nCol, /* Number of columns in table */ |
| 219229 | 220266 | const char **azCol, /* Names of table columns */ |
| 219230 | 220267 | u8 *abPK, /* PRIMARY KEY array */ |
| 219231 | 220268 | sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */ |
| 219232 | 220269 | ){ |
| 219233 | 220270 | int rc = SQLITE_OK; |
| 219234 | 220271 | char *zSql = 0; |
| 219235 | 220272 | const char *zSep = ""; |
| 219236 | - const char *zCols = "*"; | |
| 220273 | + const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*"; | |
| 219237 | 220274 | int nSql = -1; |
| 219238 | 220275 | int i; |
| 219239 | 220276 | |
| 219240 | 220277 | SessionBuffer nooptest = {0, 0, 0}; |
| 219241 | 220278 | SessionBuffer pkfield = {0, 0, 0}; |
| @@ -219250,11 +220287,10 @@ | ||
| 219250 | 220287 | "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc |
| 219251 | 220288 | ); |
| 219252 | 220289 | zCols = "tbl, ?2, stat"; |
| 219253 | 220290 | }else{ |
| 219254 | 220291 | for(i=0; i<nCol; i++){ |
| 219255 | - | |
| 219256 | 220292 | if( abPK[i] ){ |
| 219257 | 220293 | sessionAppendStr(&pkfield, zSep, &rc); |
| 219258 | 220294 | sessionAppendStr(&pkvar, zSep, &rc); |
| 219259 | 220295 | zSep = ", "; |
| 219260 | 220296 | sessionAppendIdent(&pkfield, azCol[i], &rc); |
| @@ -219457,24 +220493,32 @@ | ||
| 219457 | 220493 | const char **azCol = 0; /* Table columns */ |
| 219458 | 220494 | int i; /* Used to iterate through hash buckets */ |
| 219459 | 220495 | sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ |
| 219460 | 220496 | int nRewind = buf.nBuf; /* Initial size of write buffer */ |
| 219461 | 220497 | int nNoop; /* Size of buffer after writing tbl header */ |
| 220498 | + int bRowid = 0; | |
| 219462 | 220499 | |
| 219463 | 220500 | /* Check the table schema is still Ok. */ |
| 219464 | - rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK); | |
| 219465 | - if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ | |
| 220501 | + rc = sessionTableInfo( | |
| 220502 | + 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK, | |
| 220503 | + (pSession->bImplicitPK ? &bRowid : 0) | |
| 220504 | + ); | |
| 220505 | + if( rc==SQLITE_OK && ( | |
| 220506 | + pTab->nCol!=nCol | |
| 220507 | + || pTab->bRowid!=bRowid | |
| 220508 | + || memcmp(abPK, pTab->abPK, nCol) | |
| 220509 | + )){ | |
| 219466 | 220510 | rc = SQLITE_SCHEMA; |
| 219467 | 220511 | } |
| 219468 | 220512 | |
| 219469 | 220513 | /* Write a table header */ |
| 219470 | 220514 | sessionAppendTableHdr(&buf, bPatchset, pTab, &rc); |
| 219471 | 220515 | |
| 219472 | 220516 | /* Build and compile a statement to execute: */ |
| 219473 | 220517 | if( rc==SQLITE_OK ){ |
| 219474 | 220518 | rc = sessionSelectStmt( |
| 219475 | - db, 0, pSession->zDb, zName, nCol, azCol, abPK, &pSel | |
| 220519 | + db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel | |
| 219476 | 220520 | ); |
| 219477 | 220521 | } |
| 219478 | 220522 | |
| 219479 | 220523 | nNoop = buf.nBuf; |
| 219480 | 220524 | for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){ |
| @@ -219554,11 +220598,11 @@ | ||
| 219554 | 220598 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 219555 | 220599 | ){ |
| 219556 | 220600 | int rc; |
| 219557 | 220601 | |
| 219558 | 220602 | if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; |
| 219559 | - rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset); | |
| 220603 | + rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); | |
| 219560 | 220604 | assert( rc || pnChangeset==0 |
| 219561 | 220605 | || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize |
| 219562 | 220606 | ); |
| 219563 | 220607 | return rc; |
| 219564 | 220608 | } |
| @@ -219671,10 +220715,23 @@ | ||
| 219671 | 220715 | } |
| 219672 | 220716 | } |
| 219673 | 220717 | *(int*)pArg = pSession->bEnableSize; |
| 219674 | 220718 | break; |
| 219675 | 220719 | } |
| 220720 | + | |
| 220721 | + case SQLITE_SESSION_OBJCONFIG_ROWID: { | |
| 220722 | + int iArg = *(int*)pArg; | |
| 220723 | + if( iArg>=0 ){ | |
| 220724 | + if( pSession->pTable ){ | |
| 220725 | + rc = SQLITE_MISUSE; | |
| 220726 | + }else{ | |
| 220727 | + pSession->bImplicitPK = (iArg!=0); | |
| 220728 | + } | |
| 220729 | + } | |
| 220730 | + *(int*)pArg = pSession->bImplicitPK; | |
| 220731 | + break; | |
| 220732 | + } | |
| 219676 | 220733 | |
| 219677 | 220734 | default: |
| 219678 | 220735 | rc = SQLITE_MISUSE; |
| 219679 | 220736 | } |
| 219680 | 220737 | |
| @@ -220661,10 +221718,11 @@ | ||
| 220661 | 221718 | SessionBuffer constraints; /* Deferred constraints are stored here */ |
| 220662 | 221719 | SessionBuffer rebase; /* Rebase information (if any) here */ |
| 220663 | 221720 | u8 bRebaseStarted; /* If table header is already in rebase */ |
| 220664 | 221721 | u8 bRebase; /* True to collect rebase information */ |
| 220665 | 221722 | u8 bIgnoreNoop; /* True to ignore no-op conflicts */ |
| 221723 | + int bRowid; | |
| 220666 | 221724 | }; |
| 220667 | 221725 | |
| 220668 | 221726 | /* Number of prepared UPDATE statements to cache. */ |
| 220669 | 221727 | #define SESSION_UPDATE_CACHE_SZ 12 |
| 220670 | 221728 | |
| @@ -220911,12 +221969,13 @@ | ||
| 220911 | 221969 | static int sessionSelectRow( |
| 220912 | 221970 | sqlite3 *db, /* Database handle */ |
| 220913 | 221971 | const char *zTab, /* Table name */ |
| 220914 | 221972 | SessionApplyCtx *p /* Session changeset-apply context */ |
| 220915 | 221973 | ){ |
| 221974 | + /* TODO */ | |
| 220916 | 221975 | return sessionSelectStmt(db, p->bIgnoreNoop, |
| 220917 | - "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect | |
| 221976 | + "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect | |
| 220918 | 221977 | ); |
| 220919 | 221978 | } |
| 220920 | 221979 | |
| 220921 | 221980 | /* |
| 220922 | 221981 | ** Formulate and prepare an INSERT statement to add a record to table zTab. |
| @@ -221608,10 +222667,11 @@ | ||
| 221608 | 222667 | sApply.azCol = 0; |
| 221609 | 222668 | sApply.abPK = 0; |
| 221610 | 222669 | sApply.bStat1 = 0; |
| 221611 | 222670 | sApply.bDeferConstraints = 1; |
| 221612 | 222671 | sApply.bRebaseStarted = 0; |
| 222672 | + sApply.bRowid = 0; | |
| 221613 | 222673 | memset(&sApply.constraints, 0, sizeof(SessionBuffer)); |
| 221614 | 222674 | |
| 221615 | 222675 | /* If an xFilter() callback was specified, invoke it now. If the |
| 221616 | 222676 | ** xFilter callback returns zero, skip this table. If it returns |
| 221617 | 222677 | ** non-zero, proceed. */ |
| @@ -221627,12 +222687,12 @@ | ||
| 221627 | 222687 | }else{ |
| 221628 | 222688 | int nMinCol = 0; |
| 221629 | 222689 | int i; |
| 221630 | 222690 | |
| 221631 | 222691 | sqlite3changeset_pk(pIter, &abPK, 0); |
| 221632 | - rc = sessionTableInfo(0, | |
| 221633 | - db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK | |
| 222692 | + rc = sessionTableInfo(0, db, "main", zNew, | |
| 222693 | + &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid | |
| 221634 | 222694 | ); |
| 221635 | 222695 | if( rc!=SQLITE_OK ) break; |
| 221636 | 222696 | for(i=0; i<sApply.nCol; i++){ |
| 221637 | 222697 | if( sApply.abPK[i] ) nMinCol = i+1; |
| 221638 | 222698 | } |
| @@ -223510,29 +224570,34 @@ | ||
| 223510 | 224570 | fts5_tokenizer *pTokApi; |
| 223511 | 224571 | int bLock; /* True when table is preparing statement */ |
| 223512 | 224572 | int ePattern; /* FTS_PATTERN_XXX constant */ |
| 223513 | 224573 | |
| 223514 | 224574 | /* Values loaded from the %_config table */ |
| 224575 | + int iVersion; /* fts5 file format 'version' */ | |
| 223515 | 224576 | int iCookie; /* Incremented when %_config is modified */ |
| 223516 | 224577 | int pgsz; /* Approximate page size used in %_data */ |
| 223517 | 224578 | int nAutomerge; /* 'automerge' setting */ |
| 223518 | 224579 | int nCrisisMerge; /* Maximum allowed segments per level */ |
| 223519 | 224580 | int nUsermerge; /* 'usermerge' setting */ |
| 223520 | 224581 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 223521 | 224582 | char *zRank; /* Name of rank function */ |
| 223522 | 224583 | char *zRankArgs; /* Arguments to rank function */ |
| 224584 | + int bSecureDelete; /* 'secure-delete' */ | |
| 223523 | 224585 | |
| 223524 | 224586 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 223525 | 224587 | char **pzErrmsg; |
| 223526 | 224588 | |
| 223527 | 224589 | #ifdef SQLITE_DEBUG |
| 223528 | 224590 | int bPrefixIndex; /* True to use prefix-indexes */ |
| 223529 | 224591 | #endif |
| 223530 | 224592 | }; |
| 223531 | 224593 | |
| 223532 | -/* Current expected value of %_config table 'version' field */ | |
| 223533 | -#define FTS5_CURRENT_VERSION 4 | |
| 224594 | +/* Current expected value of %_config table 'version' field. And | |
| 224595 | +** the expected version if the 'secure-delete' option has ever been | |
| 224596 | +** set on the table. */ | |
| 224597 | +#define FTS5_CURRENT_VERSION 4 | |
| 224598 | +#define FTS5_CURRENT_VERSION_SECUREDELETE 5 | |
| 223534 | 224599 | |
| 223535 | 224600 | #define FTS5_CONTENT_NORMAL 0 |
| 223536 | 224601 | #define FTS5_CONTENT_NONE 1 |
| 223537 | 224602 | #define FTS5_CONTENT_EXTERNAL 2 |
| 223538 | 224603 | |
| @@ -223694,10 +224759,11 @@ | ||
| 223694 | 224759 | /* The following are used internally by the fts5_index.c module. They are |
| 223695 | 224760 | ** defined here only to make it easier to avoid clashes with the flags |
| 223696 | 224761 | ** above. */ |
| 223697 | 224762 | #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 |
| 223698 | 224763 | #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 |
| 224764 | +#define FTS5INDEX_QUERY_SKIPHASH 0x0040 | |
| 223699 | 224765 | |
| 223700 | 224766 | /* |
| 223701 | 224767 | ** Create/destroy an Fts5Index object. |
| 223702 | 224768 | */ |
| 223703 | 224769 | static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); |
| @@ -223848,11 +224914,11 @@ | ||
| 223848 | 224914 | static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v); |
| 223849 | 224915 | static int sqlite3Fts5GetVarintLen(u32 iVal); |
| 223850 | 224916 | static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); |
| 223851 | 224917 | static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); |
| 223852 | 224918 | |
| 223853 | -#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b) | |
| 224919 | +#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b)) | |
| 223854 | 224920 | #define fts5GetVarint sqlite3Fts5GetVarint |
| 223855 | 224921 | |
| 223856 | 224922 | #define fts5FastGetVarint32(a, iOff, nVal) { \ |
| 223857 | 224923 | nVal = (a)[iOff++]; \ |
| 223858 | 224924 | if( nVal & 0x80 ){ \ |
| @@ -227340,10 +228406,11 @@ | ||
| 227340 | 228406 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 227341 | 228407 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| 227342 | 228408 | rc = SQLITE_ERROR; |
| 227343 | 228409 | } |
| 227344 | 228410 | |
| 228411 | + assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK ); | |
| 227345 | 228412 | for(i=3; rc==SQLITE_OK && i<nArg; i++){ |
| 227346 | 228413 | const char *zOrig = azArg[i]; |
| 227347 | 228414 | const char *z; |
| 227348 | 228415 | char *zOne = 0; |
| 227349 | 228416 | char *zTwo = 0; |
| @@ -227693,10 +228760,22 @@ | ||
| 227693 | 228760 | pConfig->zRankArgs = zRankArgs; |
| 227694 | 228761 | }else if( rc==SQLITE_ERROR ){ |
| 227695 | 228762 | rc = SQLITE_OK; |
| 227696 | 228763 | *pbBadkey = 1; |
| 227697 | 228764 | } |
| 228765 | + } | |
| 228766 | + | |
| 228767 | + else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){ | |
| 228768 | + int bVal = -1; | |
| 228769 | + if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ | |
| 228770 | + bVal = sqlite3_value_int(pVal); | |
| 228771 | + } | |
| 228772 | + if( bVal<0 ){ | |
| 228773 | + *pbBadkey = 1; | |
| 228774 | + }else{ | |
| 228775 | + pConfig->bSecureDelete = (bVal ? 1 : 0); | |
| 228776 | + } | |
| 227698 | 228777 | }else{ |
| 227699 | 228778 | *pbBadkey = 1; |
| 227700 | 228779 | } |
| 227701 | 228780 | return rc; |
| 227702 | 228781 | } |
| @@ -227737,19 +228816,24 @@ | ||
| 227737 | 228816 | } |
| 227738 | 228817 | } |
| 227739 | 228818 | rc = sqlite3_finalize(p); |
| 227740 | 228819 | } |
| 227741 | 228820 | |
| 227742 | - if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ | |
| 228821 | + if( rc==SQLITE_OK | |
| 228822 | + && iVersion!=FTS5_CURRENT_VERSION | |
| 228823 | + && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE | |
| 228824 | + ){ | |
| 227743 | 228825 | rc = SQLITE_ERROR; |
| 227744 | 228826 | if( pConfig->pzErrmsg ){ |
| 227745 | 228827 | assert( 0==*pConfig->pzErrmsg ); |
| 227746 | - *pConfig->pzErrmsg = sqlite3_mprintf( | |
| 227747 | - "invalid fts5 file format (found %d, expected %d) - run 'rebuild'", | |
| 227748 | - iVersion, FTS5_CURRENT_VERSION | |
| 228828 | + *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format " | |
| 228829 | + "(found %d, expected %d or %d) - run 'rebuild'", | |
| 228830 | + iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE | |
| 227749 | 228831 | ); |
| 227750 | 228832 | } |
| 228833 | + }else{ | |
| 228834 | + pConfig->iVersion = iVersion; | |
| 227751 | 228835 | } |
| 227752 | 228836 | |
| 227753 | 228837 | if( rc==SQLITE_OK ){ |
| 227754 | 228838 | pConfig->iCookie = iCookie; |
| 227755 | 228839 | } |
| @@ -227772,10 +228856,14 @@ | ||
| 227772 | 228856 | |
| 227773 | 228857 | |
| 227774 | 228858 | |
| 227775 | 228859 | /* #include "fts5Int.h" */ |
| 227776 | 228860 | /* #include "fts5parse.h" */ |
| 228861 | + | |
| 228862 | +#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH | |
| 228863 | +# define SQLITE_FTS5_MAX_EXPR_DEPTH 256 | |
| 228864 | +#endif | |
| 227777 | 228865 | |
| 227778 | 228866 | /* |
| 227779 | 228867 | ** All token types in the generated fts5parse.h file are greater than 0. |
| 227780 | 228868 | */ |
| 227781 | 228869 | #define FTS5_EOF 0 |
| @@ -227813,15 +228901,21 @@ | ||
| 227813 | 228901 | ** FTS5_AND (nChild, apChild valid) |
| 227814 | 228902 | ** FTS5_OR (nChild, apChild valid) |
| 227815 | 228903 | ** FTS5_NOT (nChild, apChild valid) |
| 227816 | 228904 | ** FTS5_STRING (pNear valid) |
| 227817 | 228905 | ** FTS5_TERM (pNear valid) |
| 228906 | +** | |
| 228907 | +** iHeight: | |
| 228908 | +** Distance from this node to furthest leaf. This is always 0 for nodes | |
| 228909 | +** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one | |
| 228910 | +** greater than the largest child value. | |
| 227818 | 228911 | */ |
| 227819 | 228912 | struct Fts5ExprNode { |
| 227820 | 228913 | int eType; /* Node type */ |
| 227821 | 228914 | int bEof; /* True at EOF */ |
| 227822 | 228915 | int bNomatch; /* True if entry is not a match */ |
| 228916 | + int iHeight; /* Distance to tree leaf nodes */ | |
| 227823 | 228917 | |
| 227824 | 228918 | /* Next method for this node. */ |
| 227825 | 228919 | int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); |
| 227826 | 228920 | |
| 227827 | 228921 | i64 iRowid; /* Current rowid */ |
| @@ -227887,10 +228981,35 @@ | ||
| 227887 | 228981 | Fts5ExprPhrase **apPhrase; /* Array of all phrases */ |
| 227888 | 228982 | Fts5ExprNode *pExpr; /* Result of a successful parse */ |
| 227889 | 228983 | int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ |
| 227890 | 228984 | }; |
| 227891 | 228985 | |
| 228986 | +/* | |
| 228987 | +** Check that the Fts5ExprNode.iHeight variables are set correctly in | |
| 228988 | +** the expression tree passed as the only argument. | |
| 228989 | +*/ | |
| 228990 | +#ifndef NDEBUG | |
| 228991 | +static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ | |
| 228992 | + if( rc==SQLITE_OK ){ | |
| 228993 | + if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){ | |
| 228994 | + assert( p->iHeight==0 ); | |
| 228995 | + }else{ | |
| 228996 | + int ii; | |
| 228997 | + int iMaxChild = 0; | |
| 228998 | + for(ii=0; ii<p->nChild; ii++){ | |
| 228999 | + Fts5ExprNode *pChild = p->apChild[ii]; | |
| 229000 | + iMaxChild = MAX(iMaxChild, pChild->iHeight); | |
| 229001 | + assert_expr_depth_ok(SQLITE_OK, pChild); | |
| 229002 | + } | |
| 229003 | + assert( p->iHeight==iMaxChild+1 ); | |
| 229004 | + } | |
| 229005 | + } | |
| 229006 | +} | |
| 229007 | +#else | |
| 229008 | +# define assert_expr_depth_ok(rc, p) | |
| 229009 | +#endif | |
| 229010 | + | |
| 227892 | 229011 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ |
| 227893 | 229012 | va_list ap; |
| 227894 | 229013 | va_start(ap, zFmt); |
| 227895 | 229014 | if( pParse->rc==SQLITE_OK ){ |
| 227896 | 229015 | assert( pParse->zErr==0 ); |
| @@ -228000,10 +229119,12 @@ | ||
| 228000 | 229119 | do { |
| 228001 | 229120 | t = fts5ExprGetToken(&sParse, &z, &token); |
| 228002 | 229121 | sqlite3Fts5Parser(pEngine, t, token, &sParse); |
| 228003 | 229122 | }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); |
| 228004 | 229123 | sqlite3Fts5ParserFree(pEngine, fts5ParseFree); |
| 229124 | + | |
| 229125 | + assert_expr_depth_ok(sParse.rc, sParse.pExpr); | |
| 228005 | 229126 | |
| 228006 | 229127 | /* If the LHS of the MATCH expression was a user column, apply the |
| 228007 | 229128 | ** implicit column-filter. */ |
| 228008 | 229129 | if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ |
| 228009 | 229130 | int n = sizeof(Fts5Colset); |
| @@ -228163,11 +229284,11 @@ | ||
| 228163 | 229284 | |
| 228164 | 229285 | static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ |
| 228165 | 229286 | Fts5Parse sParse; |
| 228166 | 229287 | memset(&sParse, 0, sizeof(sParse)); |
| 228167 | 229288 | |
| 228168 | - if( *pp1 ){ | |
| 229289 | + if( *pp1 && p2 ){ | |
| 228169 | 229290 | Fts5Expr *p1 = *pp1; |
| 228170 | 229291 | int nPhrase = p1->nPhrase + p2->nPhrase; |
| 228171 | 229292 | |
| 228172 | 229293 | p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0); |
| 228173 | 229294 | p2->pRoot = 0; |
| @@ -228188,11 +229309,11 @@ | ||
| 228188 | 229309 | p1->apExprPhrase = ap; |
| 228189 | 229310 | } |
| 228190 | 229311 | } |
| 228191 | 229312 | sqlite3_free(p2->apExprPhrase); |
| 228192 | 229313 | sqlite3_free(p2); |
| 228193 | - }else{ | |
| 229314 | + }else if( p2 ){ | |
| 228194 | 229315 | *pp1 = p2; |
| 228195 | 229316 | } |
| 228196 | 229317 | |
| 228197 | 229318 | return sParse.rc; |
| 228198 | 229319 | } |
| @@ -229962,18 +231083,22 @@ | ||
| 229962 | 231083 | }; |
| 229963 | 231084 | } |
| 229964 | 231085 | } |
| 229965 | 231086 | |
| 229966 | 231087 | static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ |
| 231088 | + int ii = p->nChild; | |
| 229967 | 231089 | if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ |
| 229968 | 231090 | int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; |
| 229969 | 231091 | memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); |
| 229970 | 231092 | p->nChild += pSub->nChild; |
| 229971 | 231093 | sqlite3_free(pSub); |
| 229972 | 231094 | }else{ |
| 229973 | 231095 | p->apChild[p->nChild++] = pSub; |
| 229974 | 231096 | } |
| 231097 | + for( ; ii<p->nChild; ii++){ | |
| 231098 | + p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1); | |
| 231099 | + } | |
| 229975 | 231100 | } |
| 229976 | 231101 | |
| 229977 | 231102 | /* |
| 229978 | 231103 | ** This function is used when parsing LIKE or GLOB patterns against |
| 229979 | 231104 | ** trigram indexes that specify either detail=column or detail=none. |
| @@ -230000,10 +231125,11 @@ | ||
| 230000 | 231125 | nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 230001 | 231126 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 230002 | 231127 | if( pRet ){ |
| 230003 | 231128 | pRet->eType = FTS5_AND; |
| 230004 | 231129 | pRet->nChild = nTerm; |
| 231130 | + pRet->iHeight = 1; | |
| 230005 | 231131 | fts5ExprAssignXNext(pRet); |
| 230006 | 231132 | pParse->nPhrase--; |
| 230007 | 231133 | for(ii=0; ii<nTerm; ii++){ |
| 230008 | 231134 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 230009 | 231135 | &pParse->rc, sizeof(Fts5ExprPhrase) |
| @@ -230105,10 +231231,18 @@ | ||
| 230105 | 231231 | } |
| 230106 | 231232 | } |
| 230107 | 231233 | }else{ |
| 230108 | 231234 | fts5ExprAddChildren(pRet, pLeft); |
| 230109 | 231235 | fts5ExprAddChildren(pRet, pRight); |
| 231236 | + if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ | |
| 231237 | + sqlite3Fts5ParseError(pParse, | |
| 231238 | + "fts5 expression tree is too large (maximum depth %d)", | |
| 231239 | + SQLITE_FTS5_MAX_EXPR_DEPTH | |
| 231240 | + ); | |
| 231241 | + sqlite3_free(pRet); | |
| 231242 | + pRet = 0; | |
| 231243 | + } | |
| 230110 | 231244 | } |
| 230111 | 231245 | } |
| 230112 | 231246 | } |
| 230113 | 231247 | } |
| 230114 | 231248 | |
| @@ -231705,10 +232839,12 @@ | ||
| 231705 | 232839 | sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ |
| 231706 | 232840 | sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ |
| 231707 | 232841 | sqlite3_stmt *pIdxSelect; |
| 231708 | 232842 | int nRead; /* Total number of blocks read */ |
| 231709 | 232843 | |
| 232844 | + sqlite3_stmt *pDeleteFromIdx; | |
| 232845 | + | |
| 231710 | 232846 | sqlite3_stmt *pDataVersion; |
| 231711 | 232847 | i64 iStructVersion; /* data_version when pStruct read */ |
| 231712 | 232848 | Fts5Structure *pStruct; /* Current db structure (or NULL) */ |
| 231713 | 232849 | }; |
| 231714 | 232850 | |
| @@ -231797,13 +232933,10 @@ | ||
| 231797 | 232933 | ** Current leaf page number within segment. |
| 231798 | 232934 | ** |
| 231799 | 232935 | ** iLeafOffset: |
| 231800 | 232936 | ** Byte offset within the current leaf that is the first byte of the |
| 231801 | 232937 | ** position list data (one byte passed the position-list size field). |
| 231802 | -** rowid field of the current entry. Usually this is the size field of the | |
| 231803 | -** position list data. The exception is if the rowid for the current entry | |
| 231804 | -** is the last thing on the leaf page. | |
| 231805 | 232938 | ** |
| 231806 | 232939 | ** pLeaf: |
| 231807 | 232940 | ** Buffer containing current leaf page data. Set to NULL at EOF. |
| 231808 | 232941 | ** |
| 231809 | 232942 | ** iTermLeafPgno, iTermLeafOffset: |
| @@ -232358,10 +233491,11 @@ | ||
| 232358 | 233491 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; |
| 232359 | 233492 | if( i>=nData ){ |
| 232360 | 233493 | rc = FTS5_CORRUPT; |
| 232361 | 233494 | break; |
| 232362 | 233495 | } |
| 233496 | + assert( pSeg!=0 ); | |
| 232363 | 233497 | i += fts5GetVarint32(&pData[i], pSeg->iSegid); |
| 232364 | 233498 | i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); |
| 232365 | 233499 | i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); |
| 232366 | 233500 | if( pSeg->pgnoLast<pSeg->pgnoFirst ){ |
| 232367 | 233501 | rc = FTS5_CORRUPT; |
| @@ -232388,10 +233522,11 @@ | ||
| 232388 | 233522 | ** Add a level to the Fts5Structure.aLevel[] array of structure object |
| 232389 | 233523 | ** (*ppStruct). |
| 232390 | 233524 | */ |
| 232391 | 233525 | static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ |
| 232392 | 233526 | fts5StructureMakeWritable(pRc, ppStruct); |
| 233527 | + assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); | |
| 232393 | 233528 | if( *pRc==SQLITE_OK ){ |
| 232394 | 233529 | Fts5Structure *pStruct = *ppStruct; |
| 232395 | 233530 | int nLevel = pStruct->nLevel; |
| 232396 | 233531 | sqlite3_int64 nByte = ( |
| 232397 | 233532 | sizeof(Fts5Structure) + /* Main structure */ |
| @@ -232846,46 +233981,29 @@ | ||
| 232846 | 233981 | assert( pLvl->bEof==0 ); |
| 232847 | 233982 | if( iOff<=pLvl->iFirstOff ){ |
| 232848 | 233983 | pLvl->bEof = 1; |
| 232849 | 233984 | }else{ |
| 232850 | 233985 | u8 *a = pLvl->pData->p; |
| 232851 | - i64 iVal; | |
| 232852 | - int iLimit; | |
| 232853 | - int ii; | |
| 232854 | - int nZero = 0; | |
| 232855 | - | |
| 232856 | - /* Currently iOff points to the first byte of a varint. This block | |
| 232857 | - ** decrements iOff until it points to the first byte of the previous | |
| 232858 | - ** varint. Taking care not to read any memory locations that occur | |
| 232859 | - ** before the buffer in memory. */ | |
| 232860 | - iLimit = (iOff>9 ? iOff-9 : 0); | |
| 232861 | - for(iOff--; iOff>iLimit; iOff--){ | |
| 232862 | - if( (a[iOff-1] & 0x80)==0 ) break; | |
| 232863 | - } | |
| 232864 | - | |
| 232865 | - fts5GetVarint(&a[iOff], (u64*)&iVal); | |
| 232866 | - pLvl->iRowid -= iVal; | |
| 232867 | - pLvl->iLeafPgno--; | |
| 232868 | - | |
| 232869 | - /* Skip backwards past any 0x00 varints. */ | |
| 232870 | - for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){ | |
| 232871 | - nZero++; | |
| 232872 | - } | |
| 232873 | - if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){ | |
| 232874 | - /* The byte immediately before the last 0x00 byte has the 0x80 bit | |
| 232875 | - ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80 | |
| 232876 | - ** bytes before a[ii]. */ | |
| 232877 | - int bZero = 0; /* True if last 0x00 counts */ | |
| 232878 | - if( (ii-8)>=pLvl->iFirstOff ){ | |
| 232879 | - int j; | |
| 232880 | - for(j=1; j<=8 && (a[ii-j] & 0x80); j++); | |
| 232881 | - bZero = (j>8); | |
| 232882 | - } | |
| 232883 | - if( bZero==0 ) nZero--; | |
| 232884 | - } | |
| 232885 | - pLvl->iLeafPgno -= nZero; | |
| 232886 | - pLvl->iOff = iOff - nZero; | |
| 233986 | + | |
| 233987 | + pLvl->iOff = 0; | |
| 233988 | + fts5DlidxLvlNext(pLvl); | |
| 233989 | + while( 1 ){ | |
| 233990 | + int nZero = 0; | |
| 233991 | + int ii = pLvl->iOff; | |
| 233992 | + u64 delta = 0; | |
| 233993 | + | |
| 233994 | + while( a[ii]==0 ){ | |
| 233995 | + nZero++; | |
| 233996 | + ii++; | |
| 233997 | + } | |
| 233998 | + ii += sqlite3Fts5GetVarint(&a[ii], &delta); | |
| 233999 | + | |
| 234000 | + if( ii>=iOff ) break; | |
| 234001 | + pLvl->iLeafPgno += nZero+1; | |
| 234002 | + pLvl->iRowid += delta; | |
| 234003 | + pLvl->iOff = ii; | |
| 234004 | + } | |
| 232887 | 234005 | } |
| 232888 | 234006 | |
| 232889 | 234007 | return pLvl->bEof; |
| 232890 | 234008 | } |
| 232891 | 234009 | |
| @@ -233077,11 +234195,11 @@ | ||
| 233077 | 234195 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 233078 | 234196 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| 233079 | 234197 | i64 iOff = pIter->iLeafOffset; |
| 233080 | 234198 | |
| 233081 | 234199 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 233082 | - if( iOff>=pIter->pLeaf->szLeaf ){ | |
| 234200 | + while( iOff>=pIter->pLeaf->szLeaf ){ | |
| 233083 | 234201 | fts5SegIterNextPage(p, pIter); |
| 233084 | 234202 | if( pIter->pLeaf==0 ){ |
| 233085 | 234203 | if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; |
| 233086 | 234204 | return; |
| 233087 | 234205 | } |
| @@ -233176,14 +234294,16 @@ | ||
| 233176 | 234294 | if( p->rc==SQLITE_OK ){ |
| 233177 | 234295 | memset(pIter, 0, sizeof(*pIter)); |
| 233178 | 234296 | fts5SegIterSetNext(p, pIter); |
| 233179 | 234297 | pIter->pSeg = pSeg; |
| 233180 | 234298 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 233181 | - fts5SegIterNextPage(p, pIter); | |
| 234299 | + do { | |
| 234300 | + fts5SegIterNextPage(p, pIter); | |
| 234301 | + }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 ); | |
| 233182 | 234302 | } |
| 233183 | 234303 | |
| 233184 | - if( p->rc==SQLITE_OK ){ | |
| 234304 | + if( p->rc==SQLITE_OK && pIter->pLeaf ){ | |
| 233185 | 234305 | pIter->iLeafOffset = 4; |
| 233186 | 234306 | assert( pIter->pLeaf!=0 ); |
| 233187 | 234307 | assert_nc( pIter->pLeaf->nn>4 ); |
| 233188 | 234308 | assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); |
| 233189 | 234309 | pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; |
| @@ -233373,11 +234493,11 @@ | ||
| 233373 | 234493 | |
| 233374 | 234494 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 233375 | 234495 | iOff = pIter->iLeafOffset; |
| 233376 | 234496 | |
| 233377 | 234497 | /* Next entry is on the next page */ |
| 233378 | - if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ | |
| 234498 | + while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ | |
| 233379 | 234499 | fts5SegIterNextPage(p, pIter); |
| 233380 | 234500 | if( p->rc || pIter->pLeaf==0 ) return; |
| 233381 | 234501 | pIter->iRowid = 0; |
| 233382 | 234502 | iOff = 4; |
| 233383 | 234503 | } |
| @@ -233566,11 +234686,11 @@ | ||
| 233566 | 234686 | static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ |
| 233567 | 234687 | Fts5DlidxIter *pDlidx = pIter->pDlidx; |
| 233568 | 234688 | Fts5Data *pLast = 0; |
| 233569 | 234689 | int pgnoLast = 0; |
| 233570 | 234690 | |
| 233571 | - if( pDlidx ){ | |
| 234691 | + if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){ | |
| 233572 | 234692 | int iSegid = pIter->pSeg->iSegid; |
| 233573 | 234693 | pgnoLast = fts5DlidxIterPgno(pDlidx); |
| 233574 | 234694 | pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); |
| 233575 | 234695 | }else{ |
| 233576 | 234696 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| @@ -234127,11 +235247,12 @@ | ||
| 234127 | 235247 | return 0; |
| 234128 | 235248 | } |
| 234129 | 235249 | |
| 234130 | 235250 | /* |
| 234131 | 235251 | ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. |
| 234132 | -** It is an error if leaf iLeafPgno does not exist or contains no rowids. | |
| 235252 | +** It is an error if leaf iLeafPgno does not exist. Unless the db is | |
| 235253 | +** a 'secure-delete' db, if it contains no rowids then this is also an error. | |
| 234133 | 235254 | */ |
| 234134 | 235255 | static void fts5SegIterGotoPage( |
| 234135 | 235256 | Fts5Index *p, /* FTS5 backend object */ |
| 234136 | 235257 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 234137 | 235258 | int iLeafPgno |
| @@ -234142,25 +235263,27 @@ | ||
| 234142 | 235263 | p->rc = FTS5_CORRUPT; |
| 234143 | 235264 | }else{ |
| 234144 | 235265 | fts5DataRelease(pIter->pNextLeaf); |
| 234145 | 235266 | pIter->pNextLeaf = 0; |
| 234146 | 235267 | pIter->iLeafPgno = iLeafPgno-1; |
| 234147 | - fts5SegIterNextPage(p, pIter); | |
| 234148 | - assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); | |
| 234149 | 235268 | |
| 234150 | - if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ | |
| 235269 | + while( p->rc==SQLITE_OK ){ | |
| 234151 | 235270 | int iOff; |
| 234152 | - u8 *a = pIter->pLeaf->p; | |
| 234153 | - int n = pIter->pLeaf->szLeaf; | |
| 234154 | - | |
| 235271 | + fts5SegIterNextPage(p, pIter); | |
| 235272 | + if( pIter->pLeaf==0 ) break; | |
| 234155 | 235273 | iOff = fts5LeafFirstRowidOff(pIter->pLeaf); |
| 234156 | - if( iOff<4 || iOff>=n ){ | |
| 234157 | - p->rc = FTS5_CORRUPT; | |
| 234158 | - }else{ | |
| 234159 | - iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); | |
| 234160 | - pIter->iLeafOffset = iOff; | |
| 234161 | - fts5SegIterLoadNPos(p, pIter); | |
| 235274 | + if( iOff>0 ){ | |
| 235275 | + u8 *a = pIter->pLeaf->p; | |
| 235276 | + int n = pIter->pLeaf->szLeaf; | |
| 235277 | + if( iOff<4 || iOff>=n ){ | |
| 235278 | + p->rc = FTS5_CORRUPT; | |
| 235279 | + }else{ | |
| 235280 | + iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); | |
| 235281 | + pIter->iLeafOffset = iOff; | |
| 235282 | + fts5SegIterLoadNPos(p, pIter); | |
| 235283 | + } | |
| 235284 | + break; | |
| 234162 | 235285 | } |
| 234163 | 235286 | } |
| 234164 | 235287 | } |
| 234165 | 235288 | } |
| 234166 | 235289 | |
| @@ -234871,11 +235994,11 @@ | ||
| 234871 | 235994 | /* Allocate space for the new multi-seg-iterator. */ |
| 234872 | 235995 | if( p->rc==SQLITE_OK ){ |
| 234873 | 235996 | if( iLevel<0 ){ |
| 234874 | 235997 | assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); |
| 234875 | 235998 | nSeg = pStruct->nSegment; |
| 234876 | - nSeg += (p->pHash ? 1 : 0); | |
| 235999 | + nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH)); | |
| 234877 | 236000 | }else{ |
| 234878 | 236001 | nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); |
| 234879 | 236002 | } |
| 234880 | 236003 | } |
| 234881 | 236004 | *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); |
| @@ -234892,11 +236015,11 @@ | ||
| 234892 | 236015 | |
| 234893 | 236016 | /* Initialize each of the component segment iterators. */ |
| 234894 | 236017 | if( p->rc==SQLITE_OK ){ |
| 234895 | 236018 | if( iLevel<0 ){ |
| 234896 | 236019 | Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; |
| 234897 | - if( p->pHash ){ | |
| 236020 | + if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){ | |
| 234898 | 236021 | /* Add a segment iterator for the current contents of the hash table. */ |
| 234899 | 236022 | Fts5SegIter *pIter = &pNew->aSeg[iIter++]; |
| 234900 | 236023 | fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); |
| 234901 | 236024 | } |
| 234902 | 236025 | for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ |
| @@ -235647,11 +236770,11 @@ | ||
| 235647 | 236770 | fts5BufferZero(&buf); |
| 235648 | 236771 | fts5BufferGrow(&p->rc, &buf, pData->nn); |
| 235649 | 236772 | fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); |
| 235650 | 236773 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); |
| 235651 | 236774 | fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); |
| 235652 | - fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); | |
| 236775 | + fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]); | |
| 235653 | 236776 | if( p->rc==SQLITE_OK ){ |
| 235654 | 236777 | /* Set the szLeaf field */ |
| 235655 | 236778 | fts5PutU16(&buf.p[2], (u16)buf.n); |
| 235656 | 236779 | } |
| 235657 | 236780 | |
| @@ -235925,20 +237048,20 @@ | ||
| 235925 | 237048 | Fts5Index *p, /* FTS5 backend object */ |
| 235926 | 237049 | Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ |
| 235927 | 237050 | ){ |
| 235928 | 237051 | const int nCrisis = p->pConfig->nCrisisMerge; |
| 235929 | 237052 | Fts5Structure *pStruct = *ppStruct; |
| 235930 | - int iLvl = 0; | |
| 235931 | - | |
| 235932 | - assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); | |
| 235933 | - while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ | |
| 235934 | - fts5IndexMergeLevel(p, &pStruct, iLvl, 0); | |
| 235935 | - assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); | |
| 235936 | - fts5StructurePromote(p, iLvl+1, pStruct); | |
| 235937 | - iLvl++; | |
| 235938 | - } | |
| 235939 | - *ppStruct = pStruct; | |
| 237053 | + if( pStruct && pStruct->nLevel>0 ){ | |
| 237054 | + int iLvl = 0; | |
| 237055 | + while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ | |
| 237056 | + fts5IndexMergeLevel(p, &pStruct, iLvl, 0); | |
| 237057 | + assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); | |
| 237058 | + fts5StructurePromote(p, iLvl+1, pStruct); | |
| 237059 | + iLvl++; | |
| 237060 | + } | |
| 237061 | + *ppStruct = pStruct; | |
| 237062 | + } | |
| 235940 | 237063 | } |
| 235941 | 237064 | |
| 235942 | 237065 | static int fts5IndexReturn(Fts5Index *p){ |
| 235943 | 237066 | int rc = p->rc; |
| 235944 | 237067 | p->rc = SQLITE_OK; |
| @@ -235967,10 +237090,417 @@ | ||
| 235967 | 237090 | ret += i; |
| 235968 | 237091 | } |
| 235969 | 237092 | } |
| 235970 | 237093 | return ret; |
| 235971 | 237094 | } |
| 237095 | + | |
| 237096 | +/* | |
| 237097 | +** Execute the SQL statement: | |
| 237098 | +** | |
| 237099 | +** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); | |
| 237100 | +** | |
| 237101 | +** This is used when a secure-delete operation removes the last term | |
| 237102 | +** from a segment leaf page. In that case the %_idx entry is removed | |
| 237103 | +** too. This is done to ensure that if all instances of a token are | |
| 237104 | +** removed from an fts5 database in secure-delete mode, no trace of | |
| 237105 | +** the token itself remains in the database. | |
| 237106 | +*/ | |
| 237107 | +static void fts5SecureDeleteIdxEntry( | |
| 237108 | + Fts5Index *p, /* FTS5 backend object */ | |
| 237109 | + int iSegid, /* Id of segment to delete entry for */ | |
| 237110 | + int iPgno /* Page number within segment */ | |
| 237111 | +){ | |
| 237112 | + if( iPgno!=1 ){ | |
| 237113 | + assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ); | |
| 237114 | + if( p->pDeleteFromIdx==0 ){ | |
| 237115 | + fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf( | |
| 237116 | + "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", | |
| 237117 | + p->pConfig->zDb, p->pConfig->zName | |
| 237118 | + )); | |
| 237119 | + } | |
| 237120 | + if( p->rc==SQLITE_OK ){ | |
| 237121 | + sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid); | |
| 237122 | + sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno); | |
| 237123 | + sqlite3_step(p->pDeleteFromIdx); | |
| 237124 | + p->rc = sqlite3_reset(p->pDeleteFromIdx); | |
| 237125 | + } | |
| 237126 | + } | |
| 237127 | +} | |
| 237128 | + | |
| 237129 | +/* | |
| 237130 | +** This is called when a secure-delete operation removes a position-list | |
| 237131 | +** that overflows onto segment page iPgno of segment pSeg. This function | |
| 237132 | +** rewrites node iPgno, and possibly one or more of its right-hand peers, | |
| 237133 | +** to remove this portion of the position list. | |
| 237134 | +** | |
| 237135 | +** Output variable (*pbLastInDoclist) is set to true if the position-list | |
| 237136 | +** removed is followed by a new term or the end-of-segment, or false if | |
| 237137 | +** it is followed by another rowid/position list. | |
| 237138 | +*/ | |
| 237139 | +static void fts5SecureDeleteOverflow( | |
| 237140 | + Fts5Index *p, | |
| 237141 | + Fts5StructureSegment *pSeg, | |
| 237142 | + int iPgno, | |
| 237143 | + int *pbLastInDoclist | |
| 237144 | +){ | |
| 237145 | + const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); | |
| 237146 | + int pgno; | |
| 237147 | + Fts5Data *pLeaf = 0; | |
| 237148 | + assert( iPgno!=1 ); | |
| 237149 | + | |
| 237150 | + *pbLastInDoclist = 1; | |
| 237151 | + for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){ | |
| 237152 | + i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); | |
| 237153 | + int iNext = 0; | |
| 237154 | + u8 *aPg = 0; | |
| 237155 | + | |
| 237156 | + pLeaf = fts5DataRead(p, iRowid); | |
| 237157 | + if( pLeaf==0 ) break; | |
| 237158 | + aPg = pLeaf->p; | |
| 237159 | + | |
| 237160 | + iNext = fts5GetU16(&aPg[0]); | |
| 237161 | + if( iNext!=0 ){ | |
| 237162 | + *pbLastInDoclist = 0; | |
| 237163 | + } | |
| 237164 | + if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ | |
| 237165 | + fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); | |
| 237166 | + } | |
| 237167 | + | |
| 237168 | + if( iNext==0 ){ | |
| 237169 | + /* The page contains no terms or rowids. Replace it with an empty | |
| 237170 | + ** page and move on to the right-hand peer. */ | |
| 237171 | + const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; | |
| 237172 | + assert_nc( bDetailNone==0 || pLeaf->nn==4 ); | |
| 237173 | + if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); | |
| 237174 | + fts5DataRelease(pLeaf); | |
| 237175 | + pLeaf = 0; | |
| 237176 | + }else if( bDetailNone ){ | |
| 237177 | + break; | |
| 237178 | + }else if( iNext>=pLeaf->szLeaf || iNext<4 ){ | |
| 237179 | + p->rc = FTS5_CORRUPT; | |
| 237180 | + break; | |
| 237181 | + }else{ | |
| 237182 | + int nShift = iNext - 4; | |
| 237183 | + int nPg; | |
| 237184 | + | |
| 237185 | + int nIdx = 0; | |
| 237186 | + u8 *aIdx = 0; | |
| 237187 | + | |
| 237188 | + /* Unless the current page footer is 0 bytes in size (in which case | |
| 237189 | + ** the new page footer will be as well), allocate and populate a | |
| 237190 | + ** buffer containing the new page footer. Set stack variables aIdx | |
| 237191 | + ** and nIdx accordingly. */ | |
| 237192 | + if( pLeaf->nn>pLeaf->szLeaf ){ | |
| 237193 | + int iFirst = 0; | |
| 237194 | + int i1 = pLeaf->szLeaf; | |
| 237195 | + int i2 = 0; | |
| 237196 | + | |
| 237197 | + aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); | |
| 237198 | + if( aIdx==0 ) break; | |
| 237199 | + i1 += fts5GetVarint32(&aPg[i1], iFirst); | |
| 237200 | + i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); | |
| 237201 | + if( i1<pLeaf->nn ){ | |
| 237202 | + memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); | |
| 237203 | + i2 += (pLeaf->nn-i1); | |
| 237204 | + } | |
| 237205 | + nIdx = i2; | |
| 237206 | + } | |
| 237207 | + | |
| 237208 | + /* Modify the contents of buffer aPg[]. Set nPg to the new size | |
| 237209 | + ** in bytes. The new page is always smaller than the old. */ | |
| 237210 | + nPg = pLeaf->szLeaf - nShift; | |
| 237211 | + memmove(&aPg[4], &aPg[4+nShift], nPg-4); | |
| 237212 | + fts5PutU16(&aPg[2], nPg); | |
| 237213 | + if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); | |
| 237214 | + if( nIdx>0 ){ | |
| 237215 | + memcpy(&aPg[nPg], aIdx, nIdx); | |
| 237216 | + nPg += nIdx; | |
| 237217 | + } | |
| 237218 | + sqlite3_free(aIdx); | |
| 237219 | + | |
| 237220 | + /* Write the new page to disk and exit the loop */ | |
| 237221 | + assert( nPg>4 || fts5GetU16(aPg)==0 ); | |
| 237222 | + fts5DataWrite(p, iRowid, aPg, nPg); | |
| 237223 | + break; | |
| 237224 | + } | |
| 237225 | + } | |
| 237226 | + fts5DataRelease(pLeaf); | |
| 237227 | +} | |
| 237228 | + | |
| 237229 | +/* | |
| 237230 | +** Completely remove the entry that pSeg currently points to from | |
| 237231 | +** the database. | |
| 237232 | +*/ | |
| 237233 | +static void fts5DoSecureDelete( | |
| 237234 | + Fts5Index *p, | |
| 237235 | + Fts5SegIter *pSeg | |
| 237236 | +){ | |
| 237237 | + const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); | |
| 237238 | + int iSegid = pSeg->pSeg->iSegid; | |
| 237239 | + u8 *aPg = pSeg->pLeaf->p; | |
| 237240 | + int nPg = pSeg->pLeaf->nn; | |
| 237241 | + int iPgIdx = pSeg->pLeaf->szLeaf; | |
| 237242 | + | |
| 237243 | + u64 iDelta = 0; | |
| 237244 | + u64 iNextDelta = 0; | |
| 237245 | + int iNextOff = 0; | |
| 237246 | + int iOff = 0; | |
| 237247 | + int nIdx = 0; | |
| 237248 | + u8 *aIdx = 0; | |
| 237249 | + int bLastInDoclist = 0; | |
| 237250 | + int iIdx = 0; | |
| 237251 | + int iStart = 0; | |
| 237252 | + int iKeyOff = 0; | |
| 237253 | + int iPrevKeyOff = 0; | |
| 237254 | + int iDelKeyOff = 0; /* Offset of deleted key, if any */ | |
| 237255 | + | |
| 237256 | + nIdx = nPg-iPgIdx; | |
| 237257 | + aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16); | |
| 237258 | + if( p->rc ) return; | |
| 237259 | + memcpy(aIdx, &aPg[iPgIdx], nIdx); | |
| 237260 | + | |
| 237261 | + /* At this point segment iterator pSeg points to the entry | |
| 237262 | + ** this function should remove from the b-tree segment. | |
| 237263 | + ** | |
| 237264 | + ** In detail=full or detail=column mode, pSeg->iLeafOffset is the | |
| 237265 | + ** offset of the first byte in the position-list for the entry to | |
| 237266 | + ** remove. Immediately before this comes two varints that will also | |
| 237267 | + ** need to be removed: | |
| 237268 | + ** | |
| 237269 | + ** + the rowid or delta rowid value for the entry, and | |
| 237270 | + ** + the size of the position list in bytes. | |
| 237271 | + ** | |
| 237272 | + ** Or, in detail=none mode, there is a single varint prior to | |
| 237273 | + ** pSeg->iLeafOffset - the rowid or delta rowid value. | |
| 237274 | + ** | |
| 237275 | + ** This block sets the following variables: | |
| 237276 | + ** | |
| 237277 | + ** iStart: | |
| 237278 | + ** iDelta: | |
| 237279 | + */ | |
| 237280 | + { | |
| 237281 | + int iSOP; | |
| 237282 | + if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ | |
| 237283 | + iStart = pSeg->iTermLeafOffset; | |
| 237284 | + }else{ | |
| 237285 | + iStart = fts5GetU16(&aPg[0]); | |
| 237286 | + } | |
| 237287 | + | |
| 237288 | + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); | |
| 237289 | + assert_nc( iSOP<=pSeg->iLeafOffset ); | |
| 237290 | + | |
| 237291 | + if( bDetailNone ){ | |
| 237292 | + while( iSOP<pSeg->iLeafOffset ){ | |
| 237293 | + if( aPg[iSOP]==0x00 ) iSOP++; | |
| 237294 | + if( aPg[iSOP]==0x00 ) iSOP++; | |
| 237295 | + iStart = iSOP; | |
| 237296 | + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); | |
| 237297 | + } | |
| 237298 | + | |
| 237299 | + iNextOff = iSOP; | |
| 237300 | + if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; | |
| 237301 | + if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; | |
| 237302 | + | |
| 237303 | + }else{ | |
| 237304 | + int nPos = 0; | |
| 237305 | + iSOP += fts5GetVarint32(&aPg[iSOP], nPos); | |
| 237306 | + while( iSOP<pSeg->iLeafOffset ){ | |
| 237307 | + iStart = iSOP + (nPos/2); | |
| 237308 | + iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); | |
| 237309 | + iSOP += fts5GetVarint32(&aPg[iSOP], nPos); | |
| 237310 | + } | |
| 237311 | + assert_nc( iSOP==pSeg->iLeafOffset ); | |
| 237312 | + iNextOff = pSeg->iLeafOffset + pSeg->nPos; | |
| 237313 | + } | |
| 237314 | + } | |
| 237315 | + | |
| 237316 | + iOff = iStart; | |
| 237317 | + if( iNextOff>=iPgIdx ){ | |
| 237318 | + int pgno = pSeg->iLeafPgno+1; | |
| 237319 | + fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); | |
| 237320 | + iNextOff = iPgIdx; | |
| 237321 | + }else{ | |
| 237322 | + /* Set bLastInDoclist to true if the entry being removed is the last | |
| 237323 | + ** in its doclist. */ | |
| 237324 | + for(iIdx=0, iKeyOff=0; iIdx<nIdx; /* no-op */){ | |
| 237325 | + u32 iVal = 0; | |
| 237326 | + iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); | |
| 237327 | + iKeyOff += iVal; | |
| 237328 | + if( iKeyOff==iNextOff ){ | |
| 237329 | + bLastInDoclist = 1; | |
| 237330 | + } | |
| 237331 | + } | |
| 237332 | + } | |
| 237333 | + | |
| 237334 | + if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist||iNextOff==iPgIdx) ){ | |
| 237335 | + fts5PutU16(&aPg[0], 0); | |
| 237336 | + } | |
| 237337 | + | |
| 237338 | + if( bLastInDoclist==0 ){ | |
| 237339 | + if( iNextOff!=iPgIdx ){ | |
| 237340 | + iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta); | |
| 237341 | + iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); | |
| 237342 | + } | |
| 237343 | + }else if( | |
| 237344 | + iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno | |
| 237345 | + ){ | |
| 237346 | + /* The entry being removed was the only position list in its | |
| 237347 | + ** doclist. Therefore the term needs to be removed as well. */ | |
| 237348 | + int iKey = 0; | |
| 237349 | + for(iIdx=0, iKeyOff=0; iIdx<nIdx; iKey++){ | |
| 237350 | + u32 iVal = 0; | |
| 237351 | + iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); | |
| 237352 | + if( (iKeyOff+iVal)>(u32)iStart ) break; | |
| 237353 | + iKeyOff += iVal; | |
| 237354 | + } | |
| 237355 | + | |
| 237356 | + iDelKeyOff = iOff = iKeyOff; | |
| 237357 | + if( iNextOff!=iPgIdx ){ | |
| 237358 | + int nPrefix = 0; | |
| 237359 | + int nSuffix = 0; | |
| 237360 | + int nPrefix2 = 0; | |
| 237361 | + int nSuffix2 = 0; | |
| 237362 | + | |
| 237363 | + iDelKeyOff = iNextOff; | |
| 237364 | + iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); | |
| 237365 | + iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); | |
| 237366 | + | |
| 237367 | + if( iKey!=1 ){ | |
| 237368 | + iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); | |
| 237369 | + } | |
| 237370 | + iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); | |
| 237371 | + | |
| 237372 | + nPrefix = MIN(nPrefix, nPrefix2); | |
| 237373 | + nSuffix = (nPrefix2 + nSuffix2) - nPrefix; | |
| 237374 | + | |
| 237375 | + if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ | |
| 237376 | + p->rc = FTS5_CORRUPT; | |
| 237377 | + }else{ | |
| 237378 | + if( iKey!=1 ){ | |
| 237379 | + iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); | |
| 237380 | + } | |
| 237381 | + iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); | |
| 237382 | + if( nPrefix2>nPrefix ){ | |
| 237383 | + memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); | |
| 237384 | + iOff += (nPrefix2-nPrefix); | |
| 237385 | + } | |
| 237386 | + memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); | |
| 237387 | + iOff += nSuffix2; | |
| 237388 | + iNextOff += nSuffix2; | |
| 237389 | + } | |
| 237390 | + } | |
| 237391 | + }else if( iStart==4 ){ | |
| 237392 | + int iPgno; | |
| 237393 | + | |
| 237394 | + assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); | |
| 237395 | + /* The entry being removed may be the only position list in | |
| 237396 | + ** its doclist. */ | |
| 237397 | + for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ | |
| 237398 | + Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); | |
| 237399 | + int bEmpty = (pPg && pPg->nn==4); | |
| 237400 | + fts5DataRelease(pPg); | |
| 237401 | + if( bEmpty==0 ) break; | |
| 237402 | + } | |
| 237403 | + | |
| 237404 | + if( iPgno==pSeg->iTermLeafPgno ){ | |
| 237405 | + i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); | |
| 237406 | + Fts5Data *pTerm = fts5DataRead(p, iId); | |
| 237407 | + if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ | |
| 237408 | + u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; | |
| 237409 | + int nTermIdx = pTerm->nn - pTerm->szLeaf; | |
| 237410 | + int iTermIdx = 0; | |
| 237411 | + int iTermOff = 0; | |
| 237412 | + | |
| 237413 | + while( 1 ){ | |
| 237414 | + u32 iVal = 0; | |
| 237415 | + int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); | |
| 237416 | + iTermOff += iVal; | |
| 237417 | + if( (iTermIdx+nByte)>=nTermIdx ) break; | |
| 237418 | + iTermIdx += nByte; | |
| 237419 | + } | |
| 237420 | + nTermIdx = iTermIdx; | |
| 237421 | + | |
| 237422 | + memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); | |
| 237423 | + fts5PutU16(&pTerm->p[2], iTermOff); | |
| 237424 | + | |
| 237425 | + fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); | |
| 237426 | + if( nTermIdx==0 ){ | |
| 237427 | + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); | |
| 237428 | + } | |
| 237429 | + } | |
| 237430 | + fts5DataRelease(pTerm); | |
| 237431 | + } | |
| 237432 | + } | |
| 237433 | + | |
| 237434 | + if( p->rc==SQLITE_OK ){ | |
| 237435 | + const int nMove = nPg - iNextOff; | |
| 237436 | + int nShift = 0; | |
| 237437 | + | |
| 237438 | + memmove(&aPg[iOff], &aPg[iNextOff], nMove); | |
| 237439 | + iPgIdx -= (iNextOff - iOff); | |
| 237440 | + nPg = iPgIdx; | |
| 237441 | + fts5PutU16(&aPg[2], iPgIdx); | |
| 237442 | + | |
| 237443 | + nShift = iNextOff - iOff; | |
| 237444 | + for(iIdx=0, iKeyOff=0, iPrevKeyOff=0; iIdx<nIdx; /* no-op */){ | |
| 237445 | + u32 iVal = 0; | |
| 237446 | + iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); | |
| 237447 | + iKeyOff += iVal; | |
| 237448 | + if( iKeyOff!=iDelKeyOff ){ | |
| 237449 | + if( iKeyOff>iOff ){ | |
| 237450 | + iKeyOff -= nShift; | |
| 237451 | + nShift = 0; | |
| 237452 | + } | |
| 237453 | + nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOff - iPrevKeyOff); | |
| 237454 | + iPrevKeyOff = iKeyOff; | |
| 237455 | + } | |
| 237456 | + } | |
| 237457 | + | |
| 237458 | + if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ | |
| 237459 | + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); | |
| 237460 | + } | |
| 237461 | + | |
| 237462 | + assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); | |
| 237463 | + fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg,nPg); | |
| 237464 | + } | |
| 237465 | + sqlite3_free(aIdx); | |
| 237466 | +} | |
| 237467 | + | |
| 237468 | +/* | |
| 237469 | +** This is called as part of flushing a delete to disk in 'secure-delete' | |
| 237470 | +** mode. It edits the segments within the database described by argument | |
| 237471 | +** pStruct to remove the entries for term zTerm, rowid iRowid. | |
| 237472 | +*/ | |
| 237473 | +static void fts5FlushSecureDelete( | |
| 237474 | + Fts5Index *p, | |
| 237475 | + Fts5Structure *pStruct, | |
| 237476 | + const char *zTerm, | |
| 237477 | + i64 iRowid | |
| 237478 | +){ | |
| 237479 | + const int f = FTS5INDEX_QUERY_SKIPHASH; | |
| 237480 | + int nTerm = (int)strlen(zTerm); | |
| 237481 | + Fts5Iter *pIter = 0; /* Used to find term instance */ | |
| 237482 | + | |
| 237483 | + fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); | |
| 237484 | + if( fts5MultiIterEof(p, pIter)==0 ){ | |
| 237485 | + i64 iThis = fts5MultiIterRowid(pIter); | |
| 237486 | + if( iThis<iRowid ){ | |
| 237487 | + fts5MultiIterNextFrom(p, pIter, iRowid); | |
| 237488 | + } | |
| 237489 | + | |
| 237490 | + if( p->rc==SQLITE_OK | |
| 237491 | + && fts5MultiIterEof(p, pIter)==0 | |
| 237492 | + && iRowid==fts5MultiIterRowid(pIter) | |
| 237493 | + ){ | |
| 237494 | + Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; | |
| 237495 | + fts5DoSecureDelete(p, pSeg); | |
| 237496 | + } | |
| 237497 | + } | |
| 237498 | + | |
| 237499 | + fts5MultiIterFree(pIter); | |
| 237500 | +} | |
| 237501 | + | |
| 235972 | 237502 | |
| 235973 | 237503 | /* |
| 235974 | 237504 | ** Flush the contents of in-memory hash table iHash to a new level-0 |
| 235975 | 237505 | ** segment on disk. Also update the corresponding structure record. |
| 235976 | 237506 | ** |
| @@ -235990,10 +237520,11 @@ | ||
| 235990 | 237520 | fts5StructureInvalidate(p); |
| 235991 | 237521 | |
| 235992 | 237522 | if( iSegid ){ |
| 235993 | 237523 | const int pgsz = p->pConfig->pgsz; |
| 235994 | 237524 | int eDetail = p->pConfig->eDetail; |
| 237525 | + int bSecureDelete = p->pConfig->bSecureDelete; | |
| 235995 | 237526 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 235996 | 237527 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 235997 | 237528 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 235998 | 237529 | |
| 235999 | 237530 | Fts5SegWriter writer; |
| @@ -236012,44 +237543,81 @@ | ||
| 236012 | 237543 | if( p->rc==SQLITE_OK ){ |
| 236013 | 237544 | p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); |
| 236014 | 237545 | } |
| 236015 | 237546 | while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ |
| 236016 | 237547 | const char *zTerm; /* Buffer containing term */ |
| 237548 | + int nTerm; /* Size of zTerm in bytes */ | |
| 236017 | 237549 | const u8 *pDoclist; /* Pointer to doclist for this term */ |
| 236018 | 237550 | int nDoclist; /* Size of doclist in bytes */ |
| 236019 | 237551 | |
| 236020 | - /* Write the term for this entry to disk. */ | |
| 237552 | + /* Get the term and doclist for this entry. */ | |
| 236021 | 237553 | sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); |
| 236022 | - fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); | |
| 236023 | - if( p->rc!=SQLITE_OK ) break; | |
| 237554 | + nTerm = (int)strlen(zTerm); | |
| 237555 | + if( bSecureDelete==0 ){ | |
| 237556 | + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); | |
| 237557 | + if( p->rc!=SQLITE_OK ) break; | |
| 237558 | + assert( writer.bFirstRowidInPage==0 ); | |
| 237559 | + } | |
| 236024 | 237560 | |
| 236025 | - assert( writer.bFirstRowidInPage==0 ); | |
| 236026 | - if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ | |
| 237561 | + if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ | |
| 236027 | 237562 | /* The entire doclist will fit on the current leaf. */ |
| 236028 | 237563 | fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); |
| 236029 | 237564 | }else{ |
| 237565 | + int bTermWritten = !bSecureDelete; | |
| 236030 | 237566 | i64 iRowid = 0; |
| 236031 | - u64 iDelta = 0; | |
| 237567 | + i64 iPrev = 0; | |
| 236032 | 237568 | int iOff = 0; |
| 236033 | 237569 | |
| 236034 | 237570 | /* The entire doclist will not fit on this leaf. The following |
| 236035 | 237571 | ** loop iterates through the poslists that make up the current |
| 236036 | 237572 | ** doclist. */ |
| 236037 | 237573 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 237574 | + u64 iDelta = 0; | |
| 236038 | 237575 | iOff += fts5GetVarint(&pDoclist[iOff], &iDelta); |
| 236039 | 237576 | iRowid += iDelta; |
| 237577 | + | |
| 237578 | + /* If in secure delete mode, and if this entry in the poslist is | |
| 237579 | + ** in fact a delete, then edit the existing segments directly | |
| 237580 | + ** using fts5FlushSecureDelete(). */ | |
| 237581 | + if( bSecureDelete ){ | |
| 237582 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 237583 | + if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ | |
| 237584 | + fts5FlushSecureDelete(p, pStruct, zTerm, iRowid); | |
| 237585 | + iOff++; | |
| 237586 | + if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ | |
| 237587 | + iOff++; | |
| 237588 | + nDoclist = 0; | |
| 237589 | + }else{ | |
| 237590 | + continue; | |
| 237591 | + } | |
| 237592 | + } | |
| 237593 | + }else if( (pDoclist[iOff] & 0x01) ){ | |
| 237594 | + fts5FlushSecureDelete(p, pStruct, zTerm, iRowid); | |
| 237595 | + if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ | |
| 237596 | + iOff++; | |
| 237597 | + continue; | |
| 237598 | + } | |
| 237599 | + } | |
| 237600 | + } | |
| 237601 | + | |
| 237602 | + if( p->rc==SQLITE_OK && bTermWritten==0 ){ | |
| 237603 | + fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); | |
| 237604 | + bTermWritten = 1; | |
| 237605 | + assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); | |
| 237606 | + } | |
| 236040 | 237607 | |
| 236041 | 237608 | if( writer.bFirstRowidInPage ){ |
| 236042 | 237609 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 236043 | 237610 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| 236044 | 237611 | writer.bFirstRowidInPage = 0; |
| 236045 | 237612 | fts5WriteDlidxAppend(p, &writer, iRowid); |
| 236046 | - if( p->rc!=SQLITE_OK ) break; | |
| 236047 | 237613 | }else{ |
| 236048 | - pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); | |
| 237614 | + pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev); | |
| 236049 | 237615 | } |
| 237616 | + if( p->rc!=SQLITE_OK ) break; | |
| 236050 | 237617 | assert( pBuf->n<=pBuf->nSpace ); |
| 237618 | + iPrev = iRowid; | |
| 236051 | 237619 | |
| 236052 | 237620 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 236053 | 237621 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ |
| 236054 | 237622 | pBuf->p[pBuf->n++] = 0; |
| 236055 | 237623 | iOff++; |
| @@ -236104,24 +237672,27 @@ | ||
| 236104 | 237672 | if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); |
| 236105 | 237673 | } |
| 236106 | 237674 | sqlite3Fts5HashClear(pHash); |
| 236107 | 237675 | fts5WriteFinish(p, &writer, &pgnoLast); |
| 236108 | 237676 | |
| 236109 | - /* Update the Fts5Structure. It is written back to the database by the | |
| 236110 | - ** fts5StructureRelease() call below. */ | |
| 236111 | - if( pStruct->nLevel==0 ){ | |
| 236112 | - fts5StructureAddLevel(&p->rc, &pStruct); | |
| 236113 | - } | |
| 236114 | - fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); | |
| 236115 | - if( p->rc==SQLITE_OK ){ | |
| 236116 | - pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; | |
| 236117 | - pSeg->iSegid = iSegid; | |
| 236118 | - pSeg->pgnoFirst = 1; | |
| 236119 | - pSeg->pgnoLast = pgnoLast; | |
| 236120 | - pStruct->nSegment++; | |
| 236121 | - } | |
| 236122 | - fts5StructurePromote(p, 0, pStruct); | |
| 237677 | + assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); | |
| 237678 | + if( pgnoLast>0 ){ | |
| 237679 | + /* Update the Fts5Structure. It is written back to the database by the | |
| 237680 | + ** fts5StructureRelease() call below. */ | |
| 237681 | + if( pStruct->nLevel==0 ){ | |
| 237682 | + fts5StructureAddLevel(&p->rc, &pStruct); | |
| 237683 | + } | |
| 237684 | + fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); | |
| 237685 | + if( p->rc==SQLITE_OK ){ | |
| 237686 | + pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; | |
| 237687 | + pSeg->iSegid = iSegid; | |
| 237688 | + pSeg->pgnoFirst = 1; | |
| 237689 | + pSeg->pgnoLast = pgnoLast; | |
| 237690 | + pStruct->nSegment++; | |
| 237691 | + } | |
| 237692 | + fts5StructurePromote(p, 0, pStruct); | |
| 237693 | + } | |
| 236123 | 237694 | } |
| 236124 | 237695 | |
| 236125 | 237696 | fts5IndexAutomerge(p, &pStruct, pgnoLast); |
| 236126 | 237697 | fts5IndexCrisismerge(p, &pStruct); |
| 236127 | 237698 | fts5StructureWrite(p, pStruct); |
| @@ -236858,10 +238429,11 @@ | ||
| 236858 | 238429 | sqlite3_finalize(p->pDeleter); |
| 236859 | 238430 | sqlite3_finalize(p->pIdxWriter); |
| 236860 | 238431 | sqlite3_finalize(p->pIdxDeleter); |
| 236861 | 238432 | sqlite3_finalize(p->pIdxSelect); |
| 236862 | 238433 | sqlite3_finalize(p->pDataVersion); |
| 238434 | + sqlite3_finalize(p->pDeleteFromIdx); | |
| 236863 | 238435 | sqlite3Fts5HashFree(p->pHash); |
| 236864 | 238436 | sqlite3_free(p->zDataTbl); |
| 236865 | 238437 | sqlite3_free(p); |
| 236866 | 238438 | } |
| 236867 | 238439 | return rc; |
| @@ -237488,10 +239060,11 @@ | ||
| 237488 | 239060 | static void fts5IndexIntegrityCheckSegment( |
| 237489 | 239061 | Fts5Index *p, /* FTS5 backend object */ |
| 237490 | 239062 | Fts5StructureSegment *pSeg /* Segment to check internal consistency */ |
| 237491 | 239063 | ){ |
| 237492 | 239064 | Fts5Config *pConfig = p->pConfig; |
| 239065 | + int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE); | |
| 237493 | 239066 | sqlite3_stmt *pStmt = 0; |
| 237494 | 239067 | int rc2; |
| 237495 | 239068 | int iIdxPrevLeaf = pSeg->pgnoFirst-1; |
| 237496 | 239069 | int iDlidxPrevLeaf = pSeg->pgnoLast; |
| 237497 | 239070 | |
| @@ -237523,11 +239096,23 @@ | ||
| 237523 | 239096 | /* Check that the leaf contains at least one term, and that it is equal |
| 237524 | 239097 | ** to or larger than the split-key in zIdxTerm. Also check that if there |
| 237525 | 239098 | ** is also a rowid pointer within the leaf page header, it points to a |
| 237526 | 239099 | ** location before the term. */ |
| 237527 | 239100 | if( pLeaf->nn<=pLeaf->szLeaf ){ |
| 237528 | - p->rc = FTS5_CORRUPT; | |
| 239101 | + | |
| 239102 | + if( nIdxTerm==0 | |
| 239103 | + && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE | |
| 239104 | + && pLeaf->nn==pLeaf->szLeaf | |
| 239105 | + && pLeaf->nn==4 | |
| 239106 | + ){ | |
| 239107 | + /* special case - the very first page in a segment keeps its %_idx | |
| 239108 | + ** entry even if all the terms are removed from it by secure-delete | |
| 239109 | + ** operations. */ | |
| 239110 | + }else{ | |
| 239111 | + p->rc = FTS5_CORRUPT; | |
| 239112 | + } | |
| 239113 | + | |
| 237529 | 239114 | }else{ |
| 237530 | 239115 | int iOff; /* Offset of first term on leaf */ |
| 237531 | 239116 | int iRowidOff; /* Offset of first rowid on leaf */ |
| 237532 | 239117 | int nTerm; /* Size of term on leaf in bytes */ |
| 237533 | 239118 | int res; /* Comparison of term and split-key */ |
| @@ -237587,13 +239172,16 @@ | ||
| 237587 | 239172 | i64 iRowid; |
| 237588 | 239173 | int iRowidOff = fts5LeafFirstRowidOff(pLeaf); |
| 237589 | 239174 | ASSERT_SZLEAF_OK(pLeaf); |
| 237590 | 239175 | if( iRowidOff>=pLeaf->szLeaf ){ |
| 237591 | 239176 | p->rc = FTS5_CORRUPT; |
| 237592 | - }else{ | |
| 239177 | + }else if( bSecureDelete==0 || iRowidOff>0 ){ | |
| 239178 | + i64 iDlRowid = fts5DlidxIterRowid(pDlidx); | |
| 237593 | 239179 | fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); |
| 237594 | - if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT; | |
| 239180 | + if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){ | |
| 239181 | + p->rc = FTS5_CORRUPT; | |
| 239182 | + } | |
| 237595 | 239183 | } |
| 237596 | 239184 | fts5DataRelease(pLeaf); |
| 237597 | 239185 | } |
| 237598 | 239186 | } |
| 237599 | 239187 | |
| @@ -239851,10 +241439,12 @@ | ||
| 239851 | 241439 | ){ |
| 239852 | 241440 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 239853 | 241441 | Fts5Config *pConfig = pTab->p.pConfig; |
| 239854 | 241442 | int eType0; /* value_type() of apVal[0] */ |
| 239855 | 241443 | int rc = SQLITE_OK; /* Return code */ |
| 241444 | + int bUpdateOrDelete = 0; | |
| 241445 | + | |
| 239856 | 241446 | |
| 239857 | 241447 | /* A transaction must be open when this is called. */ |
| 239858 | 241448 | assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); |
| 239859 | 241449 | |
| 239860 | 241450 | assert( pVtab->zErrMsg==0 ); |
| @@ -239861,10 +241451,15 @@ | ||
| 239861 | 241451 | assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); |
| 239862 | 241452 | assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER |
| 239863 | 241453 | || sqlite3_value_type(apVal[0])==SQLITE_NULL |
| 239864 | 241454 | ); |
| 239865 | 241455 | assert( pTab->p.pConfig->pzErrmsg==0 ); |
| 241456 | + if( pConfig->pgsz==0 ){ | |
| 241457 | + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); | |
| 241458 | + if( rc!=SQLITE_OK ) return rc; | |
| 241459 | + } | |
| 241460 | + | |
| 239866 | 241461 | pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 239867 | 241462 | |
| 239868 | 241463 | /* Put any active cursors into REQUIRE_SEEK state. */ |
| 239869 | 241464 | fts5TripCursors(pTab); |
| 239870 | 241465 | |
| @@ -239913,10 +241508,11 @@ | ||
| 239913 | 241508 | |
| 239914 | 241509 | /* DELETE */ |
| 239915 | 241510 | else if( nArg==1 ){ |
| 239916 | 241511 | i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
| 239917 | 241512 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); |
| 241513 | + bUpdateOrDelete = 1; | |
| 239918 | 241514 | } |
| 239919 | 241515 | |
| 239920 | 241516 | /* INSERT or UPDATE */ |
| 239921 | 241517 | else{ |
| 239922 | 241518 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| @@ -239928,10 +241524,11 @@ | ||
| 239928 | 241524 | else if( eType0!=SQLITE_INTEGER ){ |
| 239929 | 241525 | /* If this is a REPLACE, first remove the current entry (if any) */ |
| 239930 | 241526 | if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ |
| 239931 | 241527 | i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ |
| 239932 | 241528 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); |
| 241529 | + bUpdateOrDelete = 1; | |
| 239933 | 241530 | } |
| 239934 | 241531 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 239935 | 241532 | } |
| 239936 | 241533 | |
| 239937 | 241534 | /* UPDATE */ |
| @@ -239956,13 +241553,27 @@ | ||
| 239956 | 241553 | } |
| 239957 | 241554 | }else{ |
| 239958 | 241555 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
| 239959 | 241556 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 239960 | 241557 | } |
| 241558 | + bUpdateOrDelete = 1; | |
| 239961 | 241559 | } |
| 239962 | 241560 | } |
| 239963 | 241561 | } |
| 241562 | + | |
| 241563 | + if( rc==SQLITE_OK | |
| 241564 | + && bUpdateOrDelete | |
| 241565 | + && pConfig->bSecureDelete | |
| 241566 | + && pConfig->iVersion==FTS5_CURRENT_VERSION | |
| 241567 | + ){ | |
| 241568 | + rc = sqlite3Fts5StorageConfigValue( | |
| 241569 | + pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE | |
| 241570 | + ); | |
| 241571 | + if( rc==SQLITE_OK ){ | |
| 241572 | + pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; | |
| 241573 | + } | |
| 241574 | + } | |
| 239964 | 241575 | |
| 239965 | 241576 | pTab->p.pConfig->pzErrmsg = 0; |
| 239966 | 241577 | return rc; |
| 239967 | 241578 | } |
| 239968 | 241579 | |
| @@ -240819,10 +242430,11 @@ | ||
| 240819 | 242430 | static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
| 240820 | 242431 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 240821 | 242432 | UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
| 240822 | 242433 | fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); |
| 240823 | 242434 | fts5TripCursors(pTab); |
| 242435 | + pTab->p.pConfig->pgsz = 0; | |
| 240824 | 242436 | return sqlite3Fts5StorageRollback(pTab->pStorage); |
| 240825 | 242437 | } |
| 240826 | 242438 | |
| 240827 | 242439 | /* |
| 240828 | 242440 | ** Register a new auxiliary function with global context pGlobal. |
| @@ -241021,11 +242633,11 @@ | ||
| 241021 | 242633 | int nArg, /* Number of args */ |
| 241022 | 242634 | sqlite3_value **apUnused /* Function arguments */ |
| 241023 | 242635 | ){ |
| 241024 | 242636 | assert( nArg==0 ); |
| 241025 | 242637 | UNUSED_PARAM2(nArg, apUnused); |
| 241026 | - sqlite3_result_text(pCtx, "fts5: 2023-04-10 13:20:51 49ba030080dd00b4fdf788fd3da057b333e705fa0fe37d653e2461bf96ca3785", -1, SQLITE_TRANSIENT); | |
| 242638 | + sqlite3_result_text(pCtx, "fts5: 2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0", -1, SQLITE_TRANSIENT); | |
| 241027 | 242639 | } |
| 241028 | 242640 | |
| 241029 | 242641 | /* |
| 241030 | 242642 | ** Return true if zName is the extension on one of the shadow tables used |
| 241031 | 242643 | ** by this module. |
| 241032 | 242644 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -121,10 +121,14 @@ | |
| 121 | #if defined(_MSC_VER) && !defined(_WIN64) |
| 122 | #undef SQLITE_4_BYTE_ALIGNED_MALLOC |
| 123 | #define SQLITE_4_BYTE_ALIGNED_MALLOC |
| 124 | #endif /* defined(_MSC_VER) && !defined(_WIN64) */ |
| 125 | |
| 126 | #endif /* SQLITE_MSVC_H */ |
| 127 | |
| 128 | /************** End of msvc.h ************************************************/ |
| 129 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 130 | |
| @@ -452,11 +456,11 @@ | |
| 452 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 453 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 454 | */ |
| 455 | #define SQLITE_VERSION "3.42.0" |
| 456 | #define SQLITE_VERSION_NUMBER 3042000 |
| 457 | #define SQLITE_SOURCE_ID "2023-04-10 18:44:00 4c5a3c5fb351cc1c2ce16c33314ce19c53531f09263f87456283d9d756002f9d" |
| 458 | |
| 459 | /* |
| 460 | ** CAPI3REF: Run-Time Library Version Numbers |
| 461 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 462 | ** |
| @@ -2446,32 +2450,32 @@ | |
| 2446 | ** configuration setting is never used, then the default maximum is determined |
| 2447 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2448 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2449 | ** </dl> |
| 2450 | */ |
| 2451 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 2452 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 2453 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| 2454 | #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ |
| 2455 | #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| 2456 | #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ |
| 2457 | #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ |
| 2458 | #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ |
| 2459 | #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ |
| 2460 | #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ |
| 2461 | #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ |
| 2462 | /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ |
| 2463 | #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ |
| 2464 | #define SQLITE_CONFIG_PCACHE 14 /* no-op */ |
| 2465 | #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ |
| 2466 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ |
| 2467 | #define SQLITE_CONFIG_URI 17 /* int */ |
| 2468 | #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ |
| 2469 | #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ |
| 2470 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2471 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 2472 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 2473 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2474 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2475 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2476 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2477 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2702,29 +2706,29 @@ | |
| 2702 | ** additional information. This feature can also be turned on and off |
| 2703 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2704 | ** </dd> |
| 2705 | ** |
| 2706 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2707 | ** <dt>SQLITE_DBCONFIG_DQS_DML</td> |
| 2708 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2709 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2710 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2711 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2712 | ** compile-time option. |
| 2713 | ** </dd> |
| 2714 | ** |
| 2715 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2716 | ** <dt>SQLITE_DBCONFIG_DQS_DDL</td> |
| 2717 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2718 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2719 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2720 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2721 | ** compile-time option. |
| 2722 | ** </dd> |
| 2723 | ** |
| 2724 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2725 | ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td> |
| 2726 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2727 | ** assume that database schemas are untainted by malicious content. |
| 2728 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2729 | ** takes additional defensive steps to protect the application from harm |
| 2730 | ** including: |
| @@ -2740,20 +2744,20 @@ | |
| 2740 | ** all applications are advised to turn it off if possible. This setting |
| 2741 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2742 | ** </dd> |
| 2743 | ** |
| 2744 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2745 | ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td> |
| 2746 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2747 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2748 | ** created database file to have a schema format version number (the 4-byte |
| 2749 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2750 | ** means that the resulting database file will be readable and writable by |
| 2751 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2752 | ** newly created databases are generally not understandable by SQLite versions |
| 2753 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2754 | ** is now scarcely any need to generated database files that are compatible |
| 2755 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2756 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2757 | ** ability to generate new database files that are compatible with version |
| 2758 | ** 3.0.0. |
| 2759 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2762,27 +2766,39 @@ | |
| 2762 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2763 | ** either generated columns or decending indexes. |
| 2764 | ** </dd> |
| 2765 | ** |
| 2766 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2767 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td> |
| 2768 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2769 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2770 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2771 | ** statistics. For statistics to be collected, the flag must be set on |
| 2772 | ** the database handle both when the SQL statement is prepared and when it |
| 2773 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2774 | ** by default.</dd> |
| 2775 | ** |
| 2776 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2777 | ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</td> |
| 2778 | ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option change the default order |
| 2779 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2780 | ** and work toward the beginning rather than starting at the beginning and |
| 2781 | ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the |
| 2782 | ** same as setting [PRAGMA reverse_unordered_selects]. This configuration option |
| 2783 | ** is useful for application testing.</dd> |
| 2784 | ** |
| 2785 | ** </dl> |
| 2786 | */ |
| 2787 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2788 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2800,11 +2816,11 @@ | |
| 2800 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2801 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2802 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2803 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2804 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2805 | #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ |
| 2806 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2807 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2808 | |
| 2809 | /* |
| 2810 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6547,10 +6563,17 @@ | |
| 6547 | ** ^SQLite implements this interface by calling the xSleep() |
| 6548 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6549 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6550 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6551 | ** in the previous paragraphs. |
| 6552 | */ |
| 6553 | SQLITE_API int sqlite3_sleep(int); |
| 6554 | |
| 6555 | /* |
| 6556 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -8174,13 +8197,13 @@ | |
| 8174 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 8175 | ** previously entered by the same thread. The behavior |
| 8176 | ** is undefined if the mutex is not currently entered by the |
| 8177 | ** calling thread or is not currently allocated. |
| 8178 | ** |
| 8179 | ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or |
| 8180 | ** sqlite3_mutex_leave() is a NULL pointer, then all three routines |
| 8181 | ** behave as no-ops. |
| 8182 | ** |
| 8183 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 8184 | */ |
| 8185 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 8186 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -11106,20 +11129,24 @@ | |
| 11106 | ** [sqlite3session_create()] for details. |
| 11107 | */ |
| 11108 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 11109 | |
| 11110 | /* |
| 11111 | ** CAPIREF: Conigure a Session Object |
| 11112 | ** METHOD: sqlite3_session |
| 11113 | ** |
| 11114 | ** This method is used to configure a session object after it has been |
| 11115 | ** created. At present the only valid value for the second parameter is |
| 11116 | ** [SQLITE_SESSION_OBJCONFIG_SIZE]. |
| 11117 | ** |
| 11118 | ** Arguments for sqlite3session_object_config() |
| 11119 | ** |
| 11120 | ** The following values may passed as the the 4th parameter to |
| 11121 | ** sqlite3session_object_config(). |
| 11122 | ** |
| 11123 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 11124 | ** This option is used to set, clear or query the flag that enables |
| 11125 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -11131,16 +11158,25 @@ | |
| 11131 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 11132 | ** enabled following the current call, or 0 otherwise. |
| 11133 | ** |
| 11134 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 11135 | ** the first table has been attached to the session object. |
| 11136 | */ |
| 11137 | SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); |
| 11138 | |
| 11139 | /* |
| 11140 | */ |
| 11141 | #define SQLITE_SESSION_OBJCONFIG_SIZE 1 |
| 11142 | |
| 11143 | /* |
| 11144 | ** CAPI3REF: Enable Or Disable A Session Object |
| 11145 | ** METHOD: sqlite3_session |
| 11146 | ** |
| @@ -13652,11 +13688,11 @@ | |
| 13652 | # define SQLITE_INLINE __forceinline |
| 13653 | #else |
| 13654 | # define SQLITE_NOINLINE |
| 13655 | # define SQLITE_INLINE |
| 13656 | #endif |
| 13657 | #if defined(SQLITE_COVERAGE_TEST) |
| 13658 | # undef SQLITE_INLINE |
| 13659 | # define SQLITE_INLINE |
| 13660 | #endif |
| 13661 | |
| 13662 | /* |
| @@ -18264,10 +18300,11 @@ | |
| 18264 | unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ |
| 18265 | unsigned bHasExpr:1; /* Index contains an expression, either a literal |
| 18266 | ** expression, or a reference to a VIRTUAL column */ |
| 18267 | #ifdef SQLITE_ENABLE_STAT4 |
| 18268 | int nSample; /* Number of elements in aSample[] */ |
| 18269 | int nSampleCol; /* Size of IndexSample.anEq[] and so on */ |
| 18270 | tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ |
| 18271 | IndexSample *aSample; /* Samples of the left-most key */ |
| 18272 | tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ |
| 18273 | tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ |
| @@ -20020,19 +20057,23 @@ | |
| 20020 | # define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) |
| 20021 | # define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) |
| 20022 | # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) |
| 20023 | # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) |
| 20024 | # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) |
| 20025 | #else |
| 20026 | # define sqlite3Toupper(x) toupper((unsigned char)(x)) |
| 20027 | # define sqlite3Isspace(x) isspace((unsigned char)(x)) |
| 20028 | # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) |
| 20029 | # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) |
| 20030 | # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) |
| 20031 | # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) |
| 20032 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) |
| 20033 | # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') |
| 20034 | #endif |
| 20035 | SQLITE_PRIVATE int sqlite3IsIdChar(u8); |
| 20036 | |
| 20037 | /* |
| 20038 | ** Internal function prototypes |
| @@ -20465,11 +20506,11 @@ | |
| 20465 | SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); |
| 20466 | SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); |
| 20467 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); |
| 20468 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); |
| 20469 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); |
| 20470 | SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); |
| 20471 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 20472 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 20473 | #endif |
| 20474 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); |
| 20475 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| @@ -22161,11 +22202,11 @@ | |
| 22161 | ** isalpha() 0x02 |
| 22162 | ** isdigit() 0x04 |
| 22163 | ** isalnum() 0x06 |
| 22164 | ** isxdigit() 0x08 |
| 22165 | ** toupper() 0x20 |
| 22166 | ** SQLite identifier character 0x40 |
| 22167 | ** Quote character 0x80 |
| 22168 | ** |
| 22169 | ** Bit 0x20 is set if the mapped character requires translation to upper |
| 22170 | ** case. i.e. if the character is a lower-case ASCII character. |
| 22171 | ** If x is a lower-case ASCII character, then its upper-case equivalent |
| @@ -23654,10 +23695,11 @@ | |
| 23654 | char validYMD; /* True (1) if Y,M,D are valid */ |
| 23655 | char validHMS; /* True (1) if h,m,s are valid */ |
| 23656 | char validTZ; /* True (1) if tz is valid */ |
| 23657 | char tzSet; /* Timezone was set explicitly */ |
| 23658 | char isError; /* An overflow has occurred */ |
| 23659 | }; |
| 23660 | |
| 23661 | |
| 23662 | /* |
| 23663 | ** Convert zDate into one or more integers according to the conversion |
| @@ -23968,10 +24010,15 @@ | |
| 23968 | }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){ |
| 23969 | return setDateTimeToCurrent(context, p); |
| 23970 | }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ |
| 23971 | setRawDateNumber(p, r); |
| 23972 | return 0; |
| 23973 | } |
| 23974 | return 1; |
| 23975 | } |
| 23976 | |
| 23977 | /* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. |
| @@ -24382,12 +24429,26 @@ | |
| 24382 | /* |
| 24383 | ** start of TTTTT |
| 24384 | ** |
| 24385 | ** Move the date backwards to the beginning of the current day, |
| 24386 | ** or month or year. |
| 24387 | */ |
| 24388 | if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; |
| 24389 | if( !p->validJD && !p->validYMD && !p->validHMS ) break; |
| 24390 | z += 9; |
| 24391 | computeYMD(p); |
| 24392 | p->validHMS = 1; |
| 24393 | p->h = p->m = 0; |
| @@ -24581,11 +24642,15 @@ | |
| 24581 | sqlite3_value **argv |
| 24582 | ){ |
| 24583 | DateTime x; |
| 24584 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24585 | computeJD(&x); |
| 24586 | sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); |
| 24587 | } |
| 24588 | } |
| 24589 | |
| 24590 | /* |
| 24591 | ** datetime( TIMESTRING, MOD, MOD, ...) |
| @@ -24597,12 +24662,12 @@ | |
| 24597 | int argc, |
| 24598 | sqlite3_value **argv |
| 24599 | ){ |
| 24600 | DateTime x; |
| 24601 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24602 | int Y, s; |
| 24603 | char zBuf[24]; |
| 24604 | computeYMD_HMS(&x); |
| 24605 | Y = x.Y; |
| 24606 | if( Y<0 ) Y = -Y; |
| 24607 | zBuf[1] = '0' + (Y/1000)%10; |
| 24608 | zBuf[2] = '0' + (Y/100)%10; |
| @@ -24619,19 +24684,32 @@ | |
| 24619 | zBuf[13] = '0' + (x.h)%10; |
| 24620 | zBuf[14] = ':'; |
| 24621 | zBuf[15] = '0' + (x.m/10)%10; |
| 24622 | zBuf[16] = '0' + (x.m)%10; |
| 24623 | zBuf[17] = ':'; |
| 24624 | s = (int)x.s; |
| 24625 | zBuf[18] = '0' + (s/10)%10; |
| 24626 | zBuf[19] = '0' + (s)%10; |
| 24627 | zBuf[20] = 0; |
| 24628 | if( x.Y<0 ){ |
| 24629 | zBuf[0] = '-'; |
| 24630 | sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); |
| 24631 | }else{ |
| 24632 | sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); |
| 24633 | } |
| 24634 | } |
| 24635 | } |
| 24636 | |
| 24637 | /* |
| @@ -24644,24 +24722,37 @@ | |
| 24644 | int argc, |
| 24645 | sqlite3_value **argv |
| 24646 | ){ |
| 24647 | DateTime x; |
| 24648 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24649 | int s; |
| 24650 | char zBuf[16]; |
| 24651 | computeHMS(&x); |
| 24652 | zBuf[0] = '0' + (x.h/10)%10; |
| 24653 | zBuf[1] = '0' + (x.h)%10; |
| 24654 | zBuf[2] = ':'; |
| 24655 | zBuf[3] = '0' + (x.m/10)%10; |
| 24656 | zBuf[4] = '0' + (x.m)%10; |
| 24657 | zBuf[5] = ':'; |
| 24658 | s = (int)x.s; |
| 24659 | zBuf[6] = '0' + (s/10)%10; |
| 24660 | zBuf[7] = '0' + (s)%10; |
| 24661 | zBuf[8] = 0; |
| 24662 | sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); |
| 24663 | } |
| 24664 | } |
| 24665 | |
| 24666 | /* |
| 24667 | ** date( TIMESTRING, MOD, MOD, ...) |
| @@ -24788,12 +24879,17 @@ | |
| 24788 | case 'M': { |
| 24789 | sqlite3_str_appendf(&sRes,"%02d",x.m); |
| 24790 | break; |
| 24791 | } |
| 24792 | case 's': { |
| 24793 | i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); |
| 24794 | sqlite3_str_appendf(&sRes,"%lld",iS); |
| 24795 | break; |
| 24796 | } |
| 24797 | case 'S': { |
| 24798 | sqlite3_str_appendf(&sRes,"%02d",(int)x.s); |
| 24799 | break; |
| @@ -30667,10 +30763,11 @@ | |
| 30667 | nsd = 16 + flag_altform2*10; |
| 30668 | bufpt = buf; |
| 30669 | { |
| 30670 | i64 szBufNeeded; /* Size of a temporary buffer needed */ |
| 30671 | szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; |
| 30672 | if( szBufNeeded > etBUFSIZE ){ |
| 30673 | bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); |
| 30674 | if( bufpt==0 ) return; |
| 30675 | } |
| 30676 | } |
| @@ -30684,14 +30781,16 @@ | |
| 30684 | if( e2<0 ){ |
| 30685 | *(bufpt++) = '0'; |
| 30686 | }else if( msd>0 ){ |
| 30687 | for(; e2>=0; e2--){ |
| 30688 | *(bufpt++) = et_getdigit_int(&longvalue,&msd); |
| 30689 | } |
| 30690 | }else{ |
| 30691 | for(; e2>=0; e2--){ |
| 30692 | *(bufpt++) = et_getdigit(&realvalue,&nsd); |
| 30693 | } |
| 30694 | } |
| 30695 | /* The decimal point */ |
| 30696 | if( flag_dp ){ |
| 30697 | *(bufpt++) = '.'; |
| @@ -34434,17 +34533,19 @@ | |
| 34434 | }else{ |
| 34435 | x = v; |
| 34436 | } |
| 34437 | i = sizeof(zTemp)-2; |
| 34438 | zTemp[sizeof(zTemp)-1] = 0; |
| 34439 | do{ |
| 34440 | zTemp[i--] = (x%10) + '0'; |
| 34441 | x = x/10; |
| 34442 | }while( x ); |
| 34443 | if( v<0 ) zTemp[i--] = '-'; |
| 34444 | memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); |
| 34445 | return sizeof(zTemp)-2-i; |
| 34446 | } |
| 34447 | |
| 34448 | /* |
| 34449 | ** Compare the 19-character string zNum against the text representation |
| 34450 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| @@ -34605,11 +34706,13 @@ | |
| 34605 | for(i=2; z[i]=='0'; i++){} |
| 34606 | for(k=i; sqlite3Isxdigit(z[k]); k++){ |
| 34607 | u = u*16 + sqlite3HexToInt(z[k]); |
| 34608 | } |
| 34609 | memcpy(pOut, &u, 8); |
| 34610 | return (z[k]==0 && k-i<=16) ? 0 : 2; |
| 34611 | }else |
| 34612 | #endif /* SQLITE_OMIT_HEX_INTEGER */ |
| 34613 | { |
| 34614 | return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); |
| 34615 | } |
| @@ -34641,11 +34744,11 @@ | |
| 34641 | && sqlite3Isxdigit(zNum[2]) |
| 34642 | ){ |
| 34643 | u32 u = 0; |
| 34644 | zNum += 2; |
| 34645 | while( zNum[0]=='0' ) zNum++; |
| 34646 | for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ |
| 34647 | u = u*16 + sqlite3HexToInt(zNum[i]); |
| 34648 | } |
| 34649 | if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ |
| 34650 | memcpy(pValue, &u, 4); |
| 34651 | return 1; |
| @@ -37137,11 +37240,11 @@ | |
| 37137 | # define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 37138 | # endif |
| 37139 | #endif |
| 37140 | |
| 37141 | /* Use pread() and pwrite() if they are available */ |
| 37142 | #if defined(__APPLE__) |
| 37143 | # define HAVE_PREAD 1 |
| 37144 | # define HAVE_PWRITE 1 |
| 37145 | #endif |
| 37146 | #if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) |
| 37147 | # undef USE_PREAD |
| @@ -40387,16 +40490,10 @@ | |
| 40387 | |
| 40388 | /* |
| 40389 | ** Seek to the offset passed as the second argument, then read cnt |
| 40390 | ** bytes into pBuf. Return the number of bytes actually read. |
| 40391 | ** |
| 40392 | ** NB: If you define USE_PREAD or USE_PREAD64, then it might also |
| 40393 | ** be necessary to define _XOPEN_SOURCE to be 500. This varies from |
| 40394 | ** one system to another. Since SQLite does not define USE_PREAD |
| 40395 | ** in any form by default, we will not attempt to define _XOPEN_SOURCE. |
| 40396 | ** See tickets #2741 and #2681. |
| 40397 | ** |
| 40398 | ** To avoid stomping the errno value on a failed read the lastErrno value |
| 40399 | ** is set before returning. |
| 40400 | */ |
| 40401 | static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ |
| 40402 | int got; |
| @@ -50419,11 +50516,11 @@ | |
| 50419 | &extendedParameters); |
| 50420 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50421 | if( isReadWrite ){ |
| 50422 | int rc2, isRO = 0; |
| 50423 | sqlite3BeginBenignMalloc(); |
| 50424 | rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); |
| 50425 | sqlite3EndBenignMalloc(); |
| 50426 | if( rc2==SQLITE_OK && isRO ) break; |
| 50427 | } |
| 50428 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50429 | #else |
| @@ -50436,11 +50533,11 @@ | |
| 50436 | NULL); |
| 50437 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50438 | if( isReadWrite ){ |
| 50439 | int rc2, isRO = 0; |
| 50440 | sqlite3BeginBenignMalloc(); |
| 50441 | rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); |
| 50442 | sqlite3EndBenignMalloc(); |
| 50443 | if( rc2==SQLITE_OK && isRO ) break; |
| 50444 | } |
| 50445 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50446 | #endif |
| @@ -50456,11 +50553,11 @@ | |
| 50456 | NULL); |
| 50457 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50458 | if( isReadWrite ){ |
| 50459 | int rc2, isRO = 0; |
| 50460 | sqlite3BeginBenignMalloc(); |
| 50461 | rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO); |
| 50462 | sqlite3EndBenignMalloc(); |
| 50463 | if( rc2==SQLITE_OK && isRO ) break; |
| 50464 | } |
| 50465 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50466 | } |
| @@ -50678,10 +50775,17 @@ | |
| 50678 | UNUSED_PARAMETER(pVfs); |
| 50679 | |
| 50680 | SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
| 50681 | OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", |
| 50682 | zFilename, flags, pResOut)); |
| 50683 | |
| 50684 | zConverted = winConvertFromUtf8Filename(zFilename); |
| 50685 | if( zConverted==0 ){ |
| 50686 | OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); |
| 50687 | return SQLITE_IOERR_NOMEM_BKPT; |
| @@ -52835,15 +52939,19 @@ | |
| 52835 | # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} |
| 52836 | static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){ |
| 52837 | PgHdr *pPg; |
| 52838 | unsigned char *a; |
| 52839 | int j; |
| 52840 | pPg = (PgHdr*)pLower->pExtra; |
| 52841 | printf("%3lld: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags); |
| 52842 | a = (unsigned char *)pLower->pBuf; |
| 52843 | for(j=0; j<12; j++) printf("%02x", a[j]); |
| 52844 | printf(" ptr %p\n", pPg); |
| 52845 | } |
| 52846 | static void pcacheDump(PCache *pCache){ |
| 52847 | int N; |
| 52848 | int i; |
| 52849 | sqlite3_pcache_page *pLower; |
| @@ -52852,13 +52960,12 @@ | |
| 52852 | if( pCache->pCache==0 ) return; |
| 52853 | N = sqlite3PcachePagecount(pCache); |
| 52854 | if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; |
| 52855 | for(i=1; i<=N; i++){ |
| 52856 | pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); |
| 52857 | if( pLower==0 ) continue; |
| 52858 | pcachePageTrace(i, pLower); |
| 52859 | if( ((PgHdr*)pLower)->pPage==0 ){ |
| 52860 | sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); |
| 52861 | } |
| 52862 | } |
| 52863 | } |
| 52864 | #else |
| @@ -58242,10 +58349,12 @@ | |
| 58242 | */ |
| 58243 | static int pager_truncate(Pager *pPager, Pgno nPage){ |
| 58244 | int rc = SQLITE_OK; |
| 58245 | assert( pPager->eState!=PAGER_ERROR ); |
| 58246 | assert( pPager->eState!=PAGER_READER ); |
| 58247 | |
| 58248 | if( isOpen(pPager->fd) |
| 58249 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| 58250 | ){ |
| 58251 | i64 currentSize, newSize; |
| @@ -61159,10 +61268,14 @@ | |
| 61159 | |
| 61160 | assert( !isOpen(pPager->fd) || !MEMDB ); |
| 61161 | if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ |
| 61162 | if( pgno>pPager->mxPgno ){ |
| 61163 | rc = SQLITE_FULL; |
| 61164 | goto pager_acquire_err; |
| 61165 | } |
| 61166 | if( noContent ){ |
| 61167 | /* Failure to set the bits in the InJournal bit-vectors is benign. |
| 61168 | ** It merely means that we might do some extra work to journal a |
| @@ -61323,14 +61436,16 @@ | |
| 61323 | } |
| 61324 | |
| 61325 | /* |
| 61326 | ** Release a page reference. |
| 61327 | ** |
| 61328 | ** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be |
| 61329 | ** used if we know that the page being released is not the last page. |
| 61330 | ** The btree layer always holds page1 open until the end, so these first |
| 61331 | ** to routines can be used to release any page other than BtShared.pPage1. |
| 61332 | ** |
| 61333 | ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine |
| 61334 | ** checks the total number of outstanding pages and if the number of |
| 61335 | ** pages reaches zero it drops the database lock. |
| 61336 | */ |
| @@ -61342,11 +61457,11 @@ | |
| 61342 | pagerReleaseMapPage(pPg); |
| 61343 | }else{ |
| 61344 | sqlite3PcacheRelease(pPg); |
| 61345 | } |
| 61346 | /* Do not use this routine to release the last reference to page1 */ |
| 61347 | assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); |
| 61348 | } |
| 61349 | SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ |
| 61350 | if( pPg ) sqlite3PagerUnrefNotNull(pPg); |
| 61351 | } |
| 61352 | SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){ |
| @@ -63102,17 +63217,19 @@ | |
| 63102 | ** Attempt to take an exclusive lock on the database file. If a PENDING lock |
| 63103 | ** is obtained instead, immediately release it. |
| 63104 | */ |
| 63105 | static int pagerExclusiveLock(Pager *pPager){ |
| 63106 | int rc; /* Return code */ |
| 63107 | |
| 63108 | assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); |
| 63109 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 63110 | if( rc!=SQLITE_OK ){ |
| 63111 | /* If the attempt to grab the exclusive lock failed, release the |
| 63112 | ** pending lock that may have been obtained instead. */ |
| 63113 | pagerUnlockDb(pPager, SHARED_LOCK); |
| 63114 | } |
| 63115 | |
| 63116 | return rc; |
| 63117 | } |
| 63118 | |
| @@ -64113,23 +64230,44 @@ | |
| 64113 | } |
| 64114 | |
| 64115 | assert( nByte>=8 ); |
| 64116 | assert( (nByte&0x00000007)==0 ); |
| 64117 | assert( nByte<=65536 ); |
| 64118 | |
| 64119 | if( nativeCksum ){ |
| 64120 | do { |
| 64121 | s1 += *aData++ + s2; |
| 64122 | s2 += *aData++ + s1; |
| 64123 | }while( aData<aEnd ); |
| 64124 | }else{ |
| 64125 | do { |
| 64126 | s1 += BYTESWAP32(aData[0]) + s2; |
| 64127 | s2 += BYTESWAP32(aData[1]) + s1; |
| 64128 | aData += 2; |
| 64129 | }while( aData<aEnd ); |
| 64130 | } |
| 64131 | |
| 64132 | aOut[0] = s1; |
| 64133 | aOut[1] = s2; |
| 64134 | } |
| 64135 | |
| @@ -67056,11 +67194,13 @@ | |
| 67056 | if( pWal->syncHeader ){ |
| 67057 | rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 67058 | if( rc ) return rc; |
| 67059 | } |
| 67060 | } |
| 67061 | assert( (int)pWal->szPage==szPage ); |
| 67062 | |
| 67063 | /* Setup information needed to write frames into the WAL */ |
| 67064 | w.pWal = pWal; |
| 67065 | w.pFd = pWal->pWalFd; |
| 67066 | w.iSyncPoint = 0; |
| @@ -67716,11 +67856,11 @@ | |
| 67716 | ** Cell content makes use of variable length integers. A variable |
| 67717 | ** length integer is 1 to 9 bytes where the lower 7 bits of each |
| 67718 | ** byte are used. The integer consists of all bytes that have bit 8 set and |
| 67719 | ** the first byte with bit 8 clear. The most significant byte of the integer |
| 67720 | ** appears first. A variable-length integer may not be more than 9 bytes long. |
| 67721 | ** As a special case, all 8 bytes of the 9th byte are used as data. This |
| 67722 | ** allows a 64-bit integer to be encoded in 9 bytes. |
| 67723 | ** |
| 67724 | ** 0x00 becomes 0x00000000 |
| 67725 | ** 0x7f becomes 0x0000007f |
| 67726 | ** 0x81 0x00 becomes 0x00000080 |
| @@ -68100,11 +68240,11 @@ | |
| 68100 | ** Legal values for BtCursor.curFlags |
| 68101 | */ |
| 68102 | #define BTCF_WriteFlag 0x01 /* True if a write cursor */ |
| 68103 | #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ |
| 68104 | #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ |
| 68105 | #define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ |
| 68106 | #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ |
| 68107 | #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ |
| 68108 | #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ |
| 68109 | |
| 68110 | /* |
| @@ -76202,11 +76342,11 @@ | |
| 76202 | aAfter[j] = iAfter; |
| 76203 | break; |
| 76204 | } |
| 76205 | } |
| 76206 | if( j>=nFree ){ |
| 76207 | if( nFree>=sizeof(aOfst)/sizeof(aOfst[0]) ){ |
| 76208 | for(j=0; j<nFree; j++){ |
| 76209 | freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]); |
| 76210 | } |
| 76211 | nFree = 0; |
| 76212 | } |
| @@ -76270,11 +76410,11 @@ | |
| 76270 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| 76271 | assert( nCell>=nTail ); |
| 76272 | nCell -= nTail; |
| 76273 | } |
| 76274 | |
| 76275 | pData = &aData[get2byteNotZero(&aData[hdr+5])]; |
| 76276 | if( pData<pBegin ) goto editpage_fail; |
| 76277 | if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail; |
| 76278 | |
| 76279 | /* Add cells to the start of the page */ |
| 76280 | if( iNew<iOld ){ |
| @@ -77907,11 +78047,11 @@ | |
| 77907 | return btreeOverwriteCell(pCur, &x2); |
| 77908 | } |
| 77909 | } |
| 77910 | } |
| 77911 | assert( pCur->eState==CURSOR_VALID |
| 77912 | || (pCur->eState==CURSOR_INVALID && loc) ); |
| 77913 | |
| 77914 | pPage = pCur->pPage; |
| 77915 | assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); |
| 77916 | assert( pPage->leaf || !pPage->intKey ); |
| 77917 | if( pPage->nFree<0 ){ |
| @@ -80157,17 +80297,11 @@ | |
| 80157 | assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); |
| 80158 | assert( p->bDestLocked ); |
| 80159 | assert( !isFatalError(p->rc) ); |
| 80160 | assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); |
| 80161 | assert( zSrcData ); |
| 80162 | |
| 80163 | /* Catch the case where the destination is an in-memory database and the |
| 80164 | ** page sizes of the source and destination differ. |
| 80165 | */ |
| 80166 | if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){ |
| 80167 | rc = SQLITE_READONLY; |
| 80168 | } |
| 80169 | |
| 80170 | /* This loop runs once for each destination page spanned by the source |
| 80171 | ** page. For each iteration, variable iOff is set to the byte offset |
| 80172 | ** of the destination page. |
| 80173 | */ |
| @@ -80296,11 +80430,14 @@ | |
| 80296 | /* Do not allow backup if the destination database is in WAL mode |
| 80297 | ** and the page sizes are different between source and destination */ |
| 80298 | pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); |
| 80299 | pgszDest = sqlite3BtreeGetPageSize(p->pDest); |
| 80300 | destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); |
| 80301 | if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){ |
| 80302 | rc = SQLITE_READONLY; |
| 80303 | } |
| 80304 | |
| 80305 | /* Now that there is a read-lock on the source database, query the |
| 80306 | ** source pager for the number of pages in the database. |
| @@ -80845,10 +80982,11 @@ | |
| 80845 | Mem tmp; |
| 80846 | char zBuf[100]; |
| 80847 | char *z; |
| 80848 | int i, j, incr; |
| 80849 | if( (p->flags & MEM_Str)==0 ) return 1; |
| 80850 | if( p->flags & MEM_Term ){ |
| 80851 | /* Insure that the string is properly zero-terminated. Pay particular |
| 80852 | ** attention to the case where p->n is odd */ |
| 80853 | if( p->szMalloc>0 && p->z==p->zMalloc ){ |
| 80854 | assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 ); |
| @@ -83508,10 +83646,12 @@ | |
| 83508 | static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ |
| 83509 | int nMaxArgs = *pMaxFuncArgs; |
| 83510 | Op *pOp; |
| 83511 | Parse *pParse = p->pParse; |
| 83512 | int *aLabel = pParse->aLabel; |
| 83513 | p->readOnly = 1; |
| 83514 | p->bIsReader = 0; |
| 83515 | pOp = &p->aOp[p->nOp-1]; |
| 83516 | assert( p->aOp[0].opcode==OP_Init ); |
| 83517 | while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){ |
| @@ -83567,10 +83707,11 @@ | |
| 83567 | /* The mkopcodeh.tcl script has so arranged things that the only |
| 83568 | ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to |
| 83569 | ** have non-negative values for P2. */ |
| 83570 | assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); |
| 83571 | assert( ADDR(pOp->p2)<-pParse->nLabel ); |
| 83572 | pOp->p2 = aLabel[ADDR(pOp->p2)]; |
| 83573 | } |
| 83574 | break; |
| 83575 | } |
| 83576 | } |
| @@ -84310,11 +84451,11 @@ | |
| 84310 | } |
| 84311 | } |
| 84312 | |
| 84313 | /* Return the most recently added opcode |
| 84314 | */ |
| 84315 | VdbeOp * sqlite3VdbeGetLastOp(Vdbe *p){ |
| 84316 | return sqlite3VdbeGetOp(p, p->nOp - 1); |
| 84317 | } |
| 84318 | |
| 84319 | #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) |
| 84320 | /* |
| @@ -88039,10 +88180,20 @@ | |
| 88039 | sqlite3 *db = v->db; |
| 88040 | i64 iKey2; |
| 88041 | PreUpdate preupdate; |
| 88042 | const char *zTbl = pTab->zName; |
| 88043 | static const u8 fakeSortOrder = 0; |
| 88044 | |
| 88045 | assert( db->pPreUpdate==0 ); |
| 88046 | memset(&preupdate, 0, sizeof(PreUpdate)); |
| 88047 | if( HasRowid(pTab)==0 ){ |
| 88048 | iKey1 = iKey2 = 0; |
| @@ -88055,12 +88206,12 @@ | |
| 88055 | } |
| 88056 | } |
| 88057 | |
| 88058 | assert( pCsr!=0 ); |
| 88059 | assert( pCsr->eCurType==CURTYPE_BTREE ); |
| 88060 | assert( pCsr->nField==pTab->nCol |
| 88061 | || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1) |
| 88062 | ); |
| 88063 | |
| 88064 | preupdate.v = v; |
| 88065 | preupdate.pCsr = pCsr; |
| 88066 | preupdate.op = op; |
| @@ -89429,13 +89580,13 @@ | |
| 89429 | p = (Vdbe *)pStmt; |
| 89430 | db = p->db; |
| 89431 | assert( db!=0 ); |
| 89432 | n = sqlite3_column_count(pStmt); |
| 89433 | if( N<n && N>=0 ){ |
| 89434 | N += useType*n; |
| 89435 | sqlite3_mutex_enter(db->mutex); |
| 89436 | assert( db->mallocFailed==0 ); |
| 89437 | #ifndef SQLITE_OMIT_UTF16 |
| 89438 | if( useUtf16 ){ |
| 89439 | ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); |
| 89440 | }else |
| 89441 | #endif |
| @@ -89443,11 +89594,12 @@ | |
| 89443 | ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); |
| 89444 | } |
| 89445 | /* A malloc may have failed inside of the _text() call. If this |
| 89446 | ** is the case, clear the mallocFailed flag and return NULL. |
| 89447 | */ |
| 89448 | if( db->mallocFailed ){ |
| 89449 | sqlite3OomClear(db); |
| 89450 | ret = 0; |
| 89451 | } |
| 89452 | sqlite3_mutex_leave(db->mutex); |
| 89453 | } |
| @@ -92987,11 +93139,11 @@ | |
| 92987 | } |
| 92988 | |
| 92989 | /* Opcode: Jump P1 P2 P3 * * |
| 92990 | ** |
| 92991 | ** Jump to the instruction at address P1, P2, or P3 depending on whether |
| 92992 | ** in the most recent OP_Compare instruction the P1 vector was less than |
| 92993 | ** equal to, or greater than the P2 vector, respectively. |
| 92994 | ** |
| 92995 | ** This opcode must immediately follow an OP_Compare opcode. |
| 92996 | */ |
| 92997 | case OP_Jump: { /* jump */ |
| @@ -93333,11 +93485,11 @@ | |
| 93333 | */ |
| 93334 | case OP_IfNullRow: { /* jump */ |
| 93335 | VdbeCursor *pC; |
| 93336 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 93337 | pC = p->apCsr[pOp->p1]; |
| 93338 | if( ALWAYS(pC) && pC->nullRow ){ |
| 93339 | sqlite3VdbeMemSetNull(aMem + pOp->p3); |
| 93340 | goto jump_to_p2; |
| 93341 | } |
| 93342 | break; |
| 93343 | } |
| @@ -93828,11 +93980,11 @@ | |
| 93828 | pIn1->flags |= MEM_IntReal; |
| 93829 | pIn1->flags &= ~MEM_Int; |
| 93830 | }else{ |
| 93831 | pIn1->u.r = (double)pIn1->u.i; |
| 93832 | pIn1->flags |= MEM_Real; |
| 93833 | pIn1->flags &= ~MEM_Int; |
| 93834 | } |
| 93835 | } |
| 93836 | REGISTER_TRACE((int)(pIn1-aMem), pIn1); |
| 93837 | zAffinity++; |
| 93838 | if( zAffinity[0]==0 ) break; |
| @@ -99841,11 +99993,11 @@ | |
| 99841 | *ppBlob = (sqlite3_blob *)pBlob; |
| 99842 | }else{ |
| 99843 | if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); |
| 99844 | sqlite3DbFree(db, pBlob); |
| 99845 | } |
| 99846 | sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); |
| 99847 | sqlite3DbFree(db, zErr); |
| 99848 | sqlite3ParseObjectReset(&sParse); |
| 99849 | rc = sqlite3ApiExit(db, rc); |
| 99850 | sqlite3_mutex_leave(db->mutex); |
| 99851 | return rc; |
| @@ -100000,11 +100152,11 @@ | |
| 100000 | }else{ |
| 100001 | char *zErr; |
| 100002 | ((Vdbe*)p->pStmt)->rc = SQLITE_OK; |
| 100003 | rc = blobSeekToRow(p, iRow, &zErr); |
| 100004 | if( rc!=SQLITE_OK ){ |
| 100005 | sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); |
| 100006 | sqlite3DbFree(db, zErr); |
| 100007 | } |
| 100008 | assert( rc!=SQLITE_SCHEMA ); |
| 100009 | } |
| 100010 | |
| @@ -107198,28 +107350,31 @@ | |
| 107198 | |
| 107199 | /* |
| 107200 | ** Join two expressions using an AND operator. If either expression is |
| 107201 | ** NULL, then just return the other expression. |
| 107202 | ** |
| 107203 | ** If one side or the other of the AND is known to be false, then instead |
| 107204 | ** of returning an AND expression, just return a constant expression with |
| 107205 | ** a value of false. |
| 107206 | */ |
| 107207 | SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ |
| 107208 | sqlite3 *db = pParse->db; |
| 107209 | if( pLeft==0 ){ |
| 107210 | return pRight; |
| 107211 | }else if( pRight==0 ){ |
| 107212 | return pLeft; |
| 107213 | }else if( (ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight)) |
| 107214 | && !IN_RENAME_OBJECT |
| 107215 | ){ |
| 107216 | sqlite3ExprDeferredDelete(pParse, pLeft); |
| 107217 | sqlite3ExprDeferredDelete(pParse, pRight); |
| 107218 | return sqlite3Expr(db, TK_INTEGER, "0"); |
| 107219 | }else{ |
| 107220 | return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); |
| 107221 | } |
| 107222 | } |
| 107223 | |
| 107224 | /* |
| 107225 | ** Construct a new expression node for a function with multiple |
| @@ -108460,16 +108615,21 @@ | |
| 108460 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ |
| 108461 | return exprIsConst(p, 3, iCur); |
| 108462 | } |
| 108463 | |
| 108464 | /* |
| 108465 | ** Check pExpr to see if it is an invariant constraint on data source pSrc. |
| 108466 | ** This is an optimization. False negatives will perhaps cause slower |
| 108467 | ** queries, but false positives will yield incorrect answers. So when in |
| 108468 | ** doubt, return 0. |
| 108469 | ** |
| 108470 | ** To be an invariant constraint, the following must be true: |
| 108471 | ** |
| 108472 | ** (1) pExpr cannot refer to any table other than pSrc->iCursor. |
| 108473 | ** |
| 108474 | ** (2) pExpr cannot use subqueries or non-deterministic functions. |
| 108475 | ** |
| @@ -108476,25 +108636,56 @@ | |
| 108476 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. |
| 108477 | ** (Is there some way to relax this constraint?) |
| 108478 | ** |
| 108479 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 108480 | ** (4a) pExpr must come from an ON clause.. |
| 108481 | (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 108482 | ** |
| 108483 | ** (5) If pSrc is not the right operand of a LEFT JOIN or the left |
| 108484 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 108485 | ** clause, not an ON clause. |
| 108486 | */ |
| 108487 | SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ |
| 108488 | if( pSrc->fg.jointype & JT_LTORJ ){ |
| 108489 | return 0; /* rule (3) */ |
| 108490 | } |
| 108491 | if( pSrc->fg.jointype & JT_LEFT ){ |
| 108492 | if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ |
| 108493 | if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ |
| 108494 | }else{ |
| 108495 | if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ |
| 108496 | } |
| 108497 | return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ |
| 108498 | } |
| 108499 | |
| 108500 | |
| @@ -112373,11 +112564,11 @@ | |
| 112373 | int iAgg = pExpr->iAgg; |
| 112374 | Parse *pParse = pWalker->pParse; |
| 112375 | sqlite3 *db = pParse->db; |
| 112376 | assert( iAgg>=0 ); |
| 112377 | if( pExpr->op!=TK_AGG_FUNCTION ){ |
| 112378 | if( ALWAYS(iAgg<pAggInfo->nColumn) |
| 112379 | && pAggInfo->aCol[iAgg].pCExpr==pExpr |
| 112380 | ){ |
| 112381 | pExpr = sqlite3ExprDup(db, pExpr, 0); |
| 112382 | if( pExpr ){ |
| 112383 | pAggInfo->aCol[iAgg].pCExpr = pExpr; |
| @@ -112578,11 +112769,11 @@ | |
| 112578 | findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); |
| 112579 | break; |
| 112580 | } /* endif pExpr->iTable==pItem->iCursor */ |
| 112581 | } /* end loop over pSrcList */ |
| 112582 | } |
| 112583 | return WRC_Prune; |
| 112584 | } |
| 112585 | case TK_AGG_FUNCTION: { |
| 112586 | if( (pNC->ncFlags & NC_InAggFunc)==0 |
| 112587 | && pWalker->walkerDepth==pExpr->op2 |
| 112588 | ){ |
| @@ -114075,10 +114266,23 @@ | |
| 114075 | } |
| 114076 | |
| 114077 | sqlite3_free(zQuot); |
| 114078 | return rc; |
| 114079 | } |
| 114080 | |
| 114081 | /* |
| 114082 | ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming |
| 114083 | ** it was read from the schema of database zDb. Return SQLITE_OK if |
| 114084 | ** successful. Otherwise, return an SQLite error code and leave an error |
| @@ -114123,11 +114327,21 @@ | |
| 114123 | if( pSel==0 ){ |
| 114124 | pStep->pExprList = 0; |
| 114125 | pSrc = 0; |
| 114126 | rc = SQLITE_NOMEM; |
| 114127 | }else{ |
| 114128 | sqlite3SelectPrep(pParse, pSel, 0); |
| 114129 | rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; |
| 114130 | assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); |
| 114131 | assert( pSrc==pSel->pSrc ); |
| 114132 | if( pStep->pExprList ) pSel->pEList = 0; |
| 114133 | pSel->pSrc = 0; |
| @@ -116859,17 +117073,22 @@ | |
| 116859 | if( zIndex==0 ) continue; |
| 116860 | nSample = sqlite3_column_int(pStmt, 1); |
| 116861 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 116862 | assert( pIdx==0 || pIdx->nSample==0 ); |
| 116863 | if( pIdx==0 ) continue; |
| 116864 | assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); |
| 116865 | if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ |
| 116866 | nIdxCol = pIdx->nKeyCol; |
| 116867 | }else{ |
| 116868 | nIdxCol = pIdx->nColumn; |
| 116869 | } |
| 116870 | pIdx->nSampleCol = nIdxCol; |
| 116871 | nByte = sizeof(IndexSample) * nSample; |
| 116872 | nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; |
| 116873 | nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ |
| 116874 | |
| 116875 | pIdx->aSample = sqlite3DbMallocZero(db, nByte); |
| @@ -116905,10 +117124,15 @@ | |
| 116905 | |
| 116906 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 116907 | if( zIndex==0 ) continue; |
| 116908 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 116909 | if( pIdx==0 ) continue; |
| 116910 | /* This next condition is true if data has already been loaded from |
| 116911 | ** the sqlite_stat4 table. */ |
| 116912 | nCol = pIdx->nSampleCol; |
| 116913 | if( pIdx!=pPrevIdx ){ |
| 116914 | initAvgEq(pPrevIdx); |
| @@ -116948,15 +117172,16 @@ | |
| 116948 | static int loadStat4(sqlite3 *db, const char *zDb){ |
| 116949 | int rc = SQLITE_OK; /* Result codes from subroutines */ |
| 116950 | const Table *pStat4; |
| 116951 | |
| 116952 | assert( db->lookaside.bDisable ); |
| 116953 | if( (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 |
| 116954 | && IsOrdinaryTable(pStat4) |
| 116955 | ){ |
| 116956 | rc = loadStatTbl(db, |
| 116957 | "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", |
| 116958 | "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", |
| 116959 | zDb |
| 116960 | ); |
| 116961 | } |
| 116962 | return rc; |
| @@ -118796,11 +119021,11 @@ | |
| 118796 | } |
| 118797 | |
| 118798 | if( IsOrdinaryTable(pTable) ){ |
| 118799 | sqlite3FkDelete(db, pTable); |
| 118800 | } |
| 118801 | #ifndef SQLITE_OMIT_VIRTUAL_TABLE |
| 118802 | else if( IsVirtual(pTable) ){ |
| 118803 | sqlite3VtabClear(db, pTable); |
| 118804 | } |
| 118805 | #endif |
| 118806 | else{ |
| @@ -123828,10 +124053,11 @@ | |
| 123828 | db->enc = enc; |
| 123829 | /* EVIDENCE-OF: R-08308-17224 The default collating function for all |
| 123830 | ** strings is BINARY. |
| 123831 | */ |
| 123832 | db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); |
| 123833 | } |
| 123834 | |
| 123835 | /* |
| 123836 | ** This function is responsible for invoking the collation factory callback |
| 123837 | ** or substituting a collation sequence of a different encoding when the |
| @@ -126724,11 +126950,11 @@ | |
| 126724 | |
| 126725 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 126726 | /* |
| 126727 | ** The "unknown" function is automatically substituted in place of |
| 126728 | ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN |
| 126729 | ** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used. |
| 126730 | ** When the "sqlite3" command-line shell is built using this functionality, |
| 126731 | ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries |
| 126732 | ** involving application-defined functions to be examined in a generic |
| 126733 | ** sqlite3 shell. |
| 126734 | */ |
| @@ -129027,26 +129253,26 @@ | |
| 129027 | zFrom = pFKey->pFrom->zName; |
| 129028 | nFrom = sqlite3Strlen30(zFrom); |
| 129029 | |
| 129030 | if( action==OE_Restrict ){ |
| 129031 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 129032 | Token tFrom; |
| 129033 | Token tDb; |
| 129034 | Expr *pRaise; |
| 129035 | |
| 129036 | tFrom.z = zFrom; |
| 129037 | tFrom.n = nFrom; |
| 129038 | tDb.z = db->aDb[iDb].zDbSName; |
| 129039 | tDb.n = sqlite3Strlen30(tDb.z); |
| 129040 | |
| 129041 | pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); |
| 129042 | if( pRaise ){ |
| 129043 | pRaise->affExpr = OE_Abort; |
| 129044 | } |
| 129045 | pSelect = sqlite3SelectNew(pParse, |
| 129046 | sqlite3ExprListAppend(pParse, 0, pRaise), |
| 129047 | sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom), |
| 129048 | pWhere, |
| 129049 | 0, 0, 0, 0, 0 |
| 129050 | ); |
| 129051 | pWhere = 0; |
| 129052 | } |
| @@ -137995,11 +138221,13 @@ | |
| 137995 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 137996 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 137997 | #else |
| 137998 | encoding = SQLITE_UTF8; |
| 137999 | #endif |
| 138000 | if( db->nVdbeActive>0 && encoding!=ENC(db) ){ |
| 138001 | rc = SQLITE_LOCKED; |
| 138002 | goto initone_error_out; |
| 138003 | }else{ |
| 138004 | sqlite3SetTextEncoding(db, encoding); |
| 138005 | } |
| @@ -138389,11 +138617,15 @@ | |
| 138389 | sParse.pOuterParse = db->pParse; |
| 138390 | db->pParse = &sParse; |
| 138391 | sParse.db = db; |
| 138392 | sParse.pReprepare = pReprepare; |
| 138393 | assert( ppStmt && *ppStmt==0 ); |
| 138394 | if( db->mallocFailed ) sqlite3ErrorMsg(&sParse, "out of memory"); |
| 138395 | assert( sqlite3_mutex_held(db->mutex) ); |
| 138396 | |
| 138397 | /* For a long-term use prepared statement avoid the use of |
| 138398 | ** lookaside memory. |
| 138399 | */ |
| @@ -139478,11 +139710,11 @@ | |
| 139478 | ** by a prior OP_MakeRecord. In this case nData==1 and regData |
| 139479 | ** will be completely unrelated to regOrigData. |
| 139480 | ** (2) All output columns are included in the sort record. In that |
| 139481 | ** case regData==regOrigData. |
| 139482 | ** (3) Some output columns are omitted from the sort record due to |
| 139483 | ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the |
| 139484 | ** SQLITE_ECEL_OMITREF optimization, or due to the |
| 139485 | ** SortCtx.pDeferredRowLoad optimiation. In any of these cases |
| 139486 | ** regOrigData is 0 to prevent this routine from trying to copy |
| 139487 | ** values that might not yet exist. |
| 139488 | */ |
| @@ -141079,11 +141311,11 @@ | |
| 141079 | |
| 141080 | assert( pSelect!=0 ); |
| 141081 | assert( (pSelect->selFlags & SF_Resolved)!=0 ); |
| 141082 | assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); |
| 141083 | assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); |
| 141084 | if( db->mallocFailed ) return; |
| 141085 | while( pSelect->pPrior ) pSelect = pSelect->pPrior; |
| 141086 | a = pSelect->pEList->a; |
| 141087 | memset(&sNC, 0, sizeof(sNC)); |
| 141088 | sNC.pSrcList = pSelect->pSrc; |
| 141089 | for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ |
| @@ -141124,22 +141356,20 @@ | |
| 141124 | if( sqlite3StdTypeAffinity[j]==pCol->affinity ){ |
| 141125 | zType = sqlite3StdType[j]; |
| 141126 | break; |
| 141127 | } |
| 141128 | } |
| 141129 | } |
| 141130 | } |
| 141131 | if( zType ){ |
| 141132 | i64 m = sqlite3Strlen30(zType); |
| 141133 | n = sqlite3Strlen30(pCol->zCnName); |
| 141134 | pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); |
| 141135 | if( pCol->zCnName ){ |
| 141136 | memcpy(&pCol->zCnName[n+1], zType, m+1); |
| 141137 | pCol->colFlags |= COLFLAG_HASTYPE; |
| 141138 | }else{ |
| 141139 | testcase( pCol->colFlags & COLFLAG_HASTYPE ); |
| 141140 | pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); |
| 141141 | } |
| 141142 | } |
| 141143 | pColl = sqlite3ExprCollSeq(pParse, p); |
| 141144 | if( pColl ){ |
| 141145 | assert( pTab->pIndex==0 ); |
| @@ -143870,25 +144100,50 @@ | |
| 143870 | ** |
| 143871 | ** (8) If the subquery is a compound that uses UNION, INTERSECT, |
| 143872 | ** or EXCEPT, then all of the result set columns for all arms of |
| 143873 | ** the compound must use the BINARY collating sequence. |
| 143874 | ** |
| 143875 | ** |
| 143876 | ** Return 0 if no changes are made and non-zero if one or more WHERE clause |
| 143877 | ** terms are duplicated into the subquery. |
| 143878 | */ |
| 143879 | static int pushDownWhereTerms( |
| 143880 | Parse *pParse, /* Parse context (for malloc() and error reporting) */ |
| 143881 | Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ |
| 143882 | Expr *pWhere, /* The WHERE clause of the outer query */ |
| 143883 | SrcItem *pSrc /* The subquery term of the outer FROM clause */ |
| 143884 | ){ |
| 143885 | Expr *pNew; |
| 143886 | int nChng = 0; |
| 143887 | if( pWhere==0 ) return 0; |
| 143888 | if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ) return 0; |
| 143889 | if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ) return 0; |
| 143890 | |
| 143891 | if( pSubq->pPrior ){ |
| 143892 | Select *pSel; |
| 143893 | int notUnionAll = 0; |
| 143894 | for(pSel=pSubq; pSel; pSel=pSel->pPrior){ |
| @@ -143939,15 +144194,32 @@ | |
| 143939 | |
| 143940 | if( pSubq->pLimit!=0 ){ |
| 143941 | return 0; /* restriction (3) */ |
| 143942 | } |
| 143943 | while( pWhere->op==TK_AND ){ |
| 143944 | nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); |
| 143945 | pWhere = pWhere->pLeft; |
| 143946 | } |
| 143947 | |
| 143948 | #if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */ |
| 143949 | if( isLeftJoin |
| 143950 | && (ExprHasProperty(pWhere,EP_OuterON)==0 |
| 143951 | || pWhere->w.iJoin!=iCursor) |
| 143952 | ){ |
| 143953 | return 0; /* restriction (4) */ |
| @@ -143957,11 +144229,11 @@ | |
| 143957 | ){ |
| 143958 | return 0; /* restriction (5) */ |
| 143959 | } |
| 143960 | #endif |
| 143961 | |
| 143962 | if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ |
| 143963 | nChng++; |
| 143964 | pSubq->selFlags |= SF_PushDown; |
| 143965 | while( pSubq ){ |
| 143966 | SubstContext x; |
| 143967 | pNew = sqlite3ExprDup(pParse->db, pWhere, 0); |
| @@ -144027,15 +144299,17 @@ | |
| 144027 | if( pX->pPrior && pX->op!=TK_ALL ){ |
| 144028 | /* This optimization does not work for compound subqueries that |
| 144029 | ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ |
| 144030 | return 0; |
| 144031 | } |
| 144032 | if( pX->pWin ){ |
| 144033 | /* This optimization does not work for subqueries that use window |
| 144034 | ** functions. */ |
| 144035 | return 0; |
| 144036 | } |
| 144037 | } |
| 144038 | colUsed = pItem->colUsed; |
| 144039 | if( pSub->pOrderBy ){ |
| 144040 | ExprList *pList = pSub->pOrderBy; |
| 144041 | for(j=0; j<pList->nExpr; j++){ |
| @@ -145207,16 +145481,17 @@ | |
| 145207 | assert( pAggInfo->iFirstReg==0 ); |
| 145208 | assert( pSelect!=0 ); |
| 145209 | assert( pSelect->pGroupBy!=0 ); |
| 145210 | pAggInfo->nColumn = pAggInfo->nAccumulator; |
| 145211 | if( ALWAYS(pAggInfo->nSortingColumn>0) ){ |
| 145212 | if( pAggInfo->nColumn==0 ){ |
| 145213 | pAggInfo->nSortingColumn = pSelect->pGroupBy->nExpr; |
| 145214 | }else{ |
| 145215 | pAggInfo->nSortingColumn = |
| 145216 | pAggInfo->aCol[pAggInfo->nColumn-1].iSorterColumn+1; |
| 145217 | } |
| 145218 | } |
| 145219 | analyzeAggFuncArgs(pAggInfo, pNC); |
| 145220 | #if TREETRACE_ENABLED |
| 145221 | if( sqlite3TreeTrace & 0x20 ){ |
| 145222 | IndexedExpr *pIEpr; |
| @@ -145952,11 +146227,11 @@ | |
| 145952 | assert( pItem->iCursor>=0 ); |
| 145953 | unsetJoinExpr(p->pWhere, pItem->iCursor, |
| 145954 | pTabList->a[0].fg.jointype & JT_LTORJ); |
| 145955 | } |
| 145956 | |
| 145957 | /* No futher action if this term of the FROM clause is no a subquery */ |
| 145958 | if( pSub==0 ) continue; |
| 145959 | |
| 145960 | /* Catch mismatch in the declared columns of a view and the number of |
| 145961 | ** columns in the SELECT on the RHS */ |
| 145962 | if( pTab->nCol!=pSub->pEList->nExpr ){ |
| @@ -146149,11 +146424,11 @@ | |
| 146149 | ** inside the subquery. This can help the subquery to run more efficiently. |
| 146150 | */ |
| 146151 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 146152 | && (pItem->fg.isCte==0 |
| 146153 | || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) |
| 146154 | && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem) |
| 146155 | ){ |
| 146156 | #if TREETRACE_ENABLED |
| 146157 | if( sqlite3TreeTrace & 0x4000 ){ |
| 146158 | TREETRACE(0x4000,pParse,p, |
| 146159 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| @@ -151944,11 +152219,14 @@ | |
| 151944 | default: |
| 151945 | xMethod = pMod->xRelease; |
| 151946 | break; |
| 151947 | } |
| 151948 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 151949 | rc = xMethod(pVTab->pVtab, iSavepoint); |
| 151950 | } |
| 151951 | sqlite3VtabUnlock(pVTab); |
| 151952 | } |
| 151953 | } |
| 151954 | } |
| @@ -153869,10 +154147,13 @@ | |
| 153869 | rc = WRC_Prune; |
| 153870 | reg = ++pWalker->pParse->nMem; /* Register for column value */ |
| 153871 | reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); |
| 153872 | pExpr->op = TK_REGISTER; |
| 153873 | pExpr->iTable = reg; |
| 153874 | } |
| 153875 | return rc; |
| 153876 | } |
| 153877 | |
| 153878 | /* |
| @@ -153970,11 +154251,11 @@ | |
| 153970 | /* If we survive all prior tests, that means this term is worth hinting */ |
| 153971 | pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0)); |
| 153972 | } |
| 153973 | if( pExpr!=0 ){ |
| 153974 | sWalker.xExprCallback = codeCursorHintFixExpr; |
| 153975 | sqlite3WalkExpr(&sWalker, pExpr); |
| 153976 | sqlite3VdbeAddOp4(v, OP_CursorHint, |
| 153977 | (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, |
| 153978 | (const char*)pExpr, P4_EXPR); |
| 153979 | } |
| 153980 | } |
| @@ -156836,11 +157117,11 @@ | |
| 156836 | if( op==TK_ISNULL |
| 156837 | && !ExprHasProperty(pExpr,EP_OuterON) |
| 156838 | && 0==sqlite3ExprCanBeNull(pLeft) |
| 156839 | ){ |
| 156840 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 156841 | pExpr->op = TK_TRUEFALSE; |
| 156842 | pExpr->u.zToken = "false"; |
| 156843 | ExprSetProperty(pExpr, EP_IsFalse); |
| 156844 | pTerm->prereqAll = 0; |
| 156845 | pTerm->eOperator = 0; |
| 156846 | } |
| @@ -158368,12 +158649,11 @@ | |
| 158368 | ** and to set up the WhereLevel object pLevel so that the code generator |
| 158369 | ** makes use of the automatic index. |
| 158370 | */ |
| 158371 | static SQLITE_NOINLINE void constructAutomaticIndex( |
| 158372 | Parse *pParse, /* The parsing context */ |
| 158373 | const WhereClause *pWC, /* The WHERE clause */ |
| 158374 | const SrcItem *pSrc, /* The FROM clause term to get the next index */ |
| 158375 | const Bitmask notReady, /* Mask of cursors that are not available */ |
| 158376 | WhereLevel *pLevel /* Write new index here */ |
| 158377 | ){ |
| 158378 | int nKeyCol; /* Number of columns in the constructed index */ |
| 158379 | WhereTerm *pTerm; /* A single term of the WHERE clause */ |
| @@ -158394,11 +158674,12 @@ | |
| 158394 | Bitmask extraCols; /* Bitmap of additional columns */ |
| 158395 | u8 sentWarning = 0; /* True if a warning has been issued */ |
| 158396 | u8 useBloomFilter = 0; /* True to also add a Bloom filter */ |
| 158397 | Expr *pPartial = 0; /* Partial Index Expression */ |
| 158398 | int iContinue = 0; /* Jump here to skip excluded rows */ |
| 158399 | SrcItem *pTabItem; /* FROM clause term being indexed */ |
| 158400 | int addrCounter = 0; /* Address where integer counter is initialized */ |
| 158401 | int regBase; /* Array of registers where record is assembled */ |
| 158402 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158403 | int addrExp = 0; /* Address of OP_Explain */ |
| 158404 | #endif |
| @@ -158410,10 +158691,12 @@ | |
| 158410 | addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158411 | |
| 158412 | /* Count the number of columns that will be added to the index |
| 158413 | ** and used to match WHERE clause constraints */ |
| 158414 | nKeyCol = 0; |
| 158415 | pTable = pSrc->pTab; |
| 158416 | pWCEnd = &pWC->a[pWC->nTerm]; |
| 158417 | pLoop = pLevel->pWLoop; |
| 158418 | idxCols = 0; |
| 158419 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| @@ -158420,11 +158703,11 @@ | |
| 158420 | Expr *pExpr = pTerm->pExpr; |
| 158421 | /* Make the automatic index a partial index if there are terms in the |
| 158422 | ** WHERE clause (or the ON clause of a LEFT join) that constrain which |
| 158423 | ** rows of the target table (pSrc) that can be used. */ |
| 158424 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158425 | && sqlite3ExprIsTableConstraint(pExpr, pSrc) |
| 158426 | ){ |
| 158427 | pPartial = sqlite3ExprAnd(pParse, pPartial, |
| 158428 | sqlite3ExprDup(pParse->db, pExpr, 0)); |
| 158429 | } |
| 158430 | if( termCanDriveIndex(pTerm, pSrc, notReady) ){ |
| @@ -158550,18 +158833,18 @@ | |
| 158550 | pLevel->regFilter = ++pParse->nMem; |
| 158551 | sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); |
| 158552 | } |
| 158553 | |
| 158554 | /* Fill the automatic index with content */ |
| 158555 | pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; |
| 158556 | if( pTabItem->fg.viaCoroutine ){ |
| 158557 | int regYield = pTabItem->regReturn; |
| 158558 | addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); |
| 158559 | sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); |
| 158560 | addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); |
| 158561 | VdbeCoverage(v); |
| 158562 | VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); |
| 158563 | }else{ |
| 158564 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); |
| 158565 | } |
| 158566 | if( pPartial ){ |
| 158567 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| @@ -158578,18 +158861,18 @@ | |
| 158578 | } |
| 158579 | sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v)); |
| 158580 | sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); |
| 158581 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); |
| 158582 | if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); |
| 158583 | if( pTabItem->fg.viaCoroutine ){ |
| 158584 | sqlite3VdbeChangeP2(v, addrCounter, regBase+n); |
| 158585 | testcase( pParse->db->mallocFailed ); |
| 158586 | assert( pLevel->iIdxCur>0 ); |
| 158587 | translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, |
| 158588 | pTabItem->regResult, pLevel->iIdxCur); |
| 158589 | sqlite3VdbeGoto(v, addrTop); |
| 158590 | pTabItem->fg.viaCoroutine = 0; |
| 158591 | }else{ |
| 158592 | sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); |
| 158593 | sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); |
| 158594 | } |
| 158595 | sqlite3VdbeJumpHere(v, addrTop); |
| @@ -158648,13 +158931,15 @@ | |
| 158648 | assert( v!=0 ); |
| 158649 | assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); |
| 158650 | |
| 158651 | addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158652 | do{ |
| 158653 | const SrcItem *pItem; |
| 158654 | const Table *pTab; |
| 158655 | u64 sz; |
| 158656 | sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); |
| 158657 | addrCont = sqlite3VdbeMakeLabel(pParse); |
| 158658 | iCur = pLevel->iTabCur; |
| 158659 | pLevel->regFilter = ++pParse->nMem; |
| 158660 | |
| @@ -158664,11 +158949,13 @@ | |
| 158664 | ** measure the size of the table at run-time using OP_Count with |
| 158665 | ** P3==1 and use that value to initialize the blob. But that makes |
| 158666 | ** testing complicated. By basing the blob size on the value in the |
| 158667 | ** sqlite_stat1 table, testing is much easier. |
| 158668 | */ |
| 158669 | pItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 158670 | assert( pItem!=0 ); |
| 158671 | pTab = pItem->pTab; |
| 158672 | assert( pTab!=0 ); |
| 158673 | sz = sqlite3LogEstToInt(pTab->nRowLogEst); |
| 158674 | if( sz<10000 ){ |
| @@ -158681,11 +158968,11 @@ | |
| 158681 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); |
| 158682 | pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; |
| 158683 | for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){ |
| 158684 | Expr *pExpr = pTerm->pExpr; |
| 158685 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158686 | && sqlite3ExprIsTableConstraint(pExpr, pItem) |
| 158687 | ){ |
| 158688 | sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); |
| 158689 | } |
| 158690 | } |
| 158691 | if( pLoop->wsFlags & WHERE_IPK ){ |
| @@ -163020,10 +163307,17 @@ | |
| 163020 | ** 3) Either the query must be DISTINCT, or else the ON or USING clause |
| 163021 | ** must contain a constraint that limits the scan of the table to |
| 163022 | ** at most a single row. |
| 163023 | ** 4) The table must not be referenced by any part of the query apart |
| 163024 | ** from its own USING or ON clause. |
| 163025 | ** |
| 163026 | ** For example, given: |
| 163027 | ** |
| 163028 | ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); |
| 163029 | ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); |
| @@ -163045,10 +163339,11 @@ | |
| 163045 | WhereInfo *pWInfo, |
| 163046 | Bitmask notReady |
| 163047 | ){ |
| 163048 | int i; |
| 163049 | Bitmask tabUsed; |
| 163050 | |
| 163051 | /* Preconditions checked by the caller */ |
| 163052 | assert( pWInfo->nLevel>=2 ); |
| 163053 | assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); |
| 163054 | |
| @@ -163059,10 +163354,11 @@ | |
| 163059 | |
| 163060 | tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); |
| 163061 | if( pWInfo->pOrderBy ){ |
| 163062 | tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); |
| 163063 | } |
| 163064 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 163065 | WhereTerm *pTerm, *pEnd; |
| 163066 | SrcItem *pItem; |
| 163067 | WhereLoop *pLoop; |
| 163068 | pLoop = pWInfo->a[i].pWLoop; |
| @@ -163081,10 +163377,16 @@ | |
| 163081 | || pTerm->pExpr->w.iJoin!=pItem->iCursor |
| 163082 | ){ |
| 163083 | break; |
| 163084 | } |
| 163085 | } |
| 163086 | } |
| 163087 | if( pTerm<pEnd ) continue; |
| 163088 | WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId)); |
| 163089 | notReady &= ~pLoop->maskSelf; |
| 163090 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| @@ -163480,26 +163782,49 @@ | |
| 163480 | if( pSelect && pSelect->pLimit ){ |
| 163481 | sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); |
| 163482 | } |
| 163483 | if( pParse->nErr ) goto whereBeginError; |
| 163484 | |
| 163485 | /* Special case: WHERE terms that do not refer to any tables in the join |
| 163486 | ** (constant expressions). Evaluate each such term, and jump over all the |
| 163487 | ** generated code if the result is not true. |
| 163488 | ** |
| 163489 | ** Do not do this if the expression contains non-deterministic functions |
| 163490 | ** that are not within a sub-select. This is not strictly required, but |
| 163491 | ** preserves SQLite's legacy behaviour in the following two cases: |
| 163492 | ** |
| 163493 | ** FROM ... WHERE random()>0; -- eval random() once per row |
| 163494 | ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall |
| 163495 | */ |
| 163496 | for(ii=0; ii<sWLB.pWC->nBase; ii++){ |
| 163497 | WhereTerm *pT = &sWLB.pWC->a[ii]; |
| 163498 | if( pT->wtFlags & TERM_VIRTUAL ) continue; |
| 163499 | if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){ |
| 163500 | sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); |
| 163501 | pT->wtFlags |= TERM_CODED; |
| 163502 | } |
| 163503 | } |
| 163504 | |
| 163505 | if( wctrlFlags & WHERE_WANT_DISTINCT ){ |
| @@ -163875,15 +164200,15 @@ | |
| 163875 | int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 163876 | sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); |
| 163877 | sqlite3VdbeJumpHere(v, iOnce); |
| 163878 | } |
| 163879 | } |
| 163880 | if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ |
| 163881 | if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ |
| 163882 | #ifndef SQLITE_OMIT_AUTOMATIC_INDEX |
| 163883 | constructAutomaticIndex(pParse, &pWInfo->sWC, |
| 163884 | &pTabList->a[pLevel->iFrom], notReady, pLevel); |
| 163885 | #endif |
| 163886 | }else{ |
| 163887 | sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); |
| 163888 | } |
| 163889 | if( db->mallocFailed ) goto whereBeginError; |
| @@ -165072,10 +165397,11 @@ | |
| 165072 | } |
| 165073 | } |
| 165074 | } |
| 165075 | /* no break */ deliberate_fall_through |
| 165076 | |
| 165077 | case TK_AGG_FUNCTION: |
| 165078 | case TK_COLUMN: { |
| 165079 | int iCol = -1; |
| 165080 | if( pParse->db->mallocFailed ) return WRC_Abort; |
| 165081 | if( p->pSub ){ |
| @@ -167902,11 +168228,11 @@ | |
| 167902 | #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; |
| 167903 | #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; |
| 167904 | #define YYFALLBACK 1 |
| 167905 | #define YYNSTATE 575 |
| 167906 | #define YYNRULE 403 |
| 167907 | #define YYNRULE_WITH_ACTION 341 |
| 167908 | #define YYNTOKEN 185 |
| 167909 | #define YY_MAX_SHIFT 574 |
| 167910 | #define YY_MIN_SHIFTREDUCE 833 |
| 167911 | #define YY_MAX_SHIFTREDUCE 1235 |
| 167912 | #define YY_ERROR_ACTION 1236 |
| @@ -167982,145 +168308,145 @@ | |
| 167982 | *********** Begin parsing tables **********************************************/ |
| 167983 | #define YY_ACTTAB_COUNT (2096) |
| 167984 | static const YYACTIONTYPE yy_action[] = { |
| 167985 | /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, |
| 167986 | /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, |
| 167987 | /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1521, 71, |
| 167988 | /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, |
| 167989 | /* 40 */ 397, 71, 71, 125, 126, 80, 1213, 1213, 1047, 1050, |
| 167990 | /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, |
| 167991 | /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, |
| 167992 | /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, |
| 167993 | /* 80 */ 417, 523, 142, 125, 126, 80, 1213, 1213, 1047, 1050, |
| 167994 | /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, |
| 167995 | /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, |
| 167996 | /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, |
| 167997 | /* 120 */ 442, 1562, 376, 1564, 1189, 375, 1160, 565, 1160, 565, |
| 167998 | /* 130 */ 409, 1562, 537, 259, 226, 444, 101, 145, 449, 316, |
| 167999 | /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, |
| 168000 | /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1213, 1213, 1047, |
| 168001 | /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, |
| 168002 | /* 170 */ 294, 1189, 339, 448, 120, 120, 120, 119, 116, 444, |
| 168003 | /* 180 */ 127, 1189, 1190, 1189, 148, 441, 440, 568, 119, 116, |
| 168004 | /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, |
| 168005 | /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, |
| 168006 | /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, |
| 168007 | /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1189, 1190, |
| 168008 | /* 230 */ 1189, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, |
| 168009 | /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168010 | /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, |
| 168011 | /* 260 */ 1213, 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, |
| 168012 | /* 270 */ 124, 124, 1275, 522, 222, 1189, 568, 409, 224, 514, |
| 168013 | /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, |
| 168014 | /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1189, 133, |
| 168015 | /* 300 */ 133, 125, 126, 80, 1213, 1213, 1047, 1050, 1037, 1037, |
| 168016 | /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, |
| 168017 | /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, |
| 168018 | /* 330 */ 1189, 373, 1189, 1190, 1189, 252, 1429, 399, 504, 501, |
| 168019 | /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, |
| 168020 | /* 350 */ 460, 328, 360, 394, 1233, 1189, 1190, 1189, 563, 568, |
| 168021 | /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, |
| 168022 | /* 370 */ 116, 444, 284, 284, 369, 1575, 1601, 441, 440, 154, |
| 168023 | /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1189, 1190, 1189, |
| 168024 | /* 390 */ 85, 1219, 271, 557, 543, 515, 1556, 568, 98, 1218, |
| 168025 | /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1213, 1213, 1047, |
| 168026 | /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, |
| 168027 | /* 420 */ 13, 13, 1024, 507, 1220, 1189, 1220, 549, 109, 109, |
| 168028 | /* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569, |
| 168029 | /* 440 */ 445, 430, 1547, 1014, 325, 551, 1189, 270, 287, 368, |
| 168030 | /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, |
| 168031 | /* 460 */ 316, 559, 1607, 122, 122, 122, 122, 121, 121, 120, |
| 168032 | /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, |
| 168033 | /* 480 */ 284, 284, 1189, 1190, 1189, 1155, 568, 1606, 409, 899, |
| 168034 | /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1155, 516, |
| 168035 | /* 500 */ 413, 1155, 552, 1189, 1190, 1189, 568, 544, 1549, 51, |
| 168036 | /* 510 */ 51, 214, 125, 126, 80, 1213, 1213, 1047, 1050, 1037, |
| 168037 | /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1189, 474, 135, |
| 168038 | /* 530 */ 135, 409, 284, 284, 1485, 505, 121, 121, 120, 120, |
| 168039 | /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1556, |
| 168040 | /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1213, 1213, |
| 168041 | /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168042 | /* 570 */ 1550, 122, 122, 122, 122, 121, 121, 120, 120, 120, |
| 168043 | /* 580 */ 119, 116, 444, 485, 1189, 1190, 1189, 482, 281, 1263, |
| 168044 | /* 590 */ 955, 252, 1189, 373, 504, 501, 500, 1189, 340, 570, |
| 168045 | /* 600 */ 1189, 570, 409, 292, 499, 955, 874, 191, 480, 316, |
| 168046 | /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, |
| 168047 | /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, |
| 168048 | /* 630 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168049 | /* 640 */ 124, 409, 394, 1133, 1189, 867, 100, 284, 284, 1189, |
| 168050 | /* 650 */ 1190, 1189, 373, 1090, 1189, 1190, 1189, 1189, 1190, 1189, |
| 168051 | /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1213, 1213, |
| 168052 | /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168053 | /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, |
| 168054 | /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1155, 228, 1189, |
| 168055 | /* 700 */ 157, 1189, 1190, 1189, 1548, 13, 13, 301, 955, 1228, |
| 168056 | /* 710 */ 1155, 153, 409, 1155, 373, 1578, 1173, 5, 369, 1575, |
| 168057 | /* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121, |
| 168058 | /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, |
| 168059 | /* 740 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168060 | /* 750 */ 124, 409, 208, 567, 1189, 1025, 1189, 1190, 1189, 1189, |
| 168061 | /* 760 */ 388, 850, 155, 1547, 286, 402, 1095, 1095, 488, 568, |
| 168062 | /* 770 */ 465, 342, 1315, 1315, 1547, 125, 126, 80, 1213, 1213, |
| 168063 | /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168064 | /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, |
| 168065 | /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, |
| 168066 | /* 810 */ 528, 1189, 1190, 1189, 13, 13, 1189, 1190, 1189, 1293, |
| 168067 | /* 820 */ 463, 1263, 409, 1313, 1313, 1547, 1010, 453, 452, 200, |
| 168068 | /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, |
| 168069 | /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, |
| 168070 | /* 850 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168071 | /* 860 */ 124, 409, 227, 1070, 1155, 284, 284, 419, 312, 278, |
| 168072 | /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1155, 565, 568, |
| 168073 | /* 880 */ 1155, 1192, 565, 1595, 565, 125, 126, 80, 1213, 1213, |
| 168074 | /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168075 | /* 900 */ 453, 1477, 13, 13, 1531, 122, 122, 122, 122, 121, |
| 168076 | /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, |
| 168077 | /* 920 */ 1581, 574, 2, 1241, 838, 839, 840, 1557, 317, 1208, |
| 168078 | /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1192, |
| 168079 | /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, |
| 168080 | /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1213, |
| 168081 | /* 960 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168082 | /* 970 */ 124, 568, 284, 284, 568, 1209, 409, 573, 313, 1241, |
| 168083 | /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637, |
| 168084 | /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, |
| 168085 | /* 1000 */ 1321, 104, 80, 1213, 1213, 1047, 1050, 1037, 1037, 123, |
| 168086 | /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, |
| 168087 | /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1111, 284, 284, |
| 168088 | /* 1030 */ 428, 448, 1520, 1209, 439, 284, 284, 1484, 1348, 311, |
| 168089 | /* 1040 */ 474, 565, 1112, 969, 491, 491, 217, 1259, 565, 1533, |
| 168090 | /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1113, 519, 122, |
| 168091 | /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168092 | /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568, |
| 168093 | /* 1080 */ 1490, 568, 284, 284, 97, 526, 491, 448, 911, 1322, |
| 168094 | /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1490, 1492, |
| 168095 | /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, |
| 168096 | /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1213, |
| 168097 | /* 1120 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168098 | /* 1130 */ 124, 347, 409, 862, 1529, 1209, 125, 126, 80, 1213, |
| 168099 | /* 1140 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168100 | /* 1150 */ 124, 1134, 1635, 474, 1635, 371, 125, 114, 80, 1213, |
| 168101 | /* 1160 */ 1213, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168102 | /* 1170 */ 124, 1490, 329, 474, 331, 122, 122, 122, 122, 121, |
| 168103 | /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, |
| 168104 | /* 1190 */ 1290, 862, 464, 1209, 436, 122, 122, 122, 122, 121, |
| 168105 | /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1134, 1636, |
| 168106 | /* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121, |
| 168107 | /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, |
| 168108 | /* 1230 */ 1132, 1415, 1554, 1555, 1327, 409, 6, 6, 1166, 1264, |
| 168109 | /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, |
| 168110 | /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, |
| 168111 | /* 1260 */ 126, 80, 1213, 1213, 1047, 1050, 1037, 1037, 123, 123, |
| 168112 | /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1189, 1415, |
| 168113 | /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1132, 1553, 847, |
| 168114 | /* 1290 */ 1166, 407, 6, 568, 321, 1155, 470, 44, 44, 1552, |
| 168115 | /* 1300 */ 1111, 426, 234, 6, 323, 256, 540, 256, 1155, 431, |
| 168116 | /* 1310 */ 568, 1155, 322, 17, 487, 1112, 58, 58, 122, 122, |
| 168117 | /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, |
| 168118 | /* 1330 */ 1113, 216, 481, 59, 59, 1189, 1190, 1189, 111, 560, |
| 168119 | /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, |
| 168120 | /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1092, |
| 168121 | /* 1360 */ 568, 293, 568, 1092, 531, 568, 870, 8, 60, 60, |
| 168122 | /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, |
| 168123 | /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, |
| 168124 | /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, |
| 168125 | /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534, |
| 168126 | /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024, |
| @@ -168127,73 +168453,73 @@ | |
| 168127 | /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66, |
| 168128 | /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177, |
| 168129 | /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, |
| 168130 | /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, |
| 168131 | /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, |
| 168132 | /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1580, 1177, 447, |
| 168133 | /* 1480 */ 69, 69, 288, 97, 108, 1536, 106, 392, 392, 391, |
| 168134 | /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, |
| 168135 | /* 1500 */ 4, 568, 152, 30, 38, 568, 1129, 234, 396, 323, |
| 168136 | /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, |
| 168137 | /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, |
| 168138 | /* 1530 */ 568, 289, 1509, 568, 31, 1508, 568, 445, 338, 483, |
| 168139 | /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1077, 557, |
| 168140 | /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, |
| 168141 | /* 1560 */ 161, 1569, 557, 535, 568, 319, 568, 348, 536, 1007, |
| 168142 | /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, |
| 168143 | /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, |
| 168144 | /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, |
| 168145 | /* 1600 */ 162, 162, 156, 156, 568, 110, 1077, 445, 569, 445, |
| 168146 | /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, |
| 168147 | /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, |
| 168148 | /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, |
| 168149 | /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1177, |
| 168150 | /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, |
| 168151 | /* 1660 */ 391, 273, 389, 568, 1138, 847, 568, 1073, 568, 258, |
| 168152 | /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, |
| 168153 | /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74, |
| 168154 | /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, |
| 168155 | /* 1700 */ 973, 1089, 1088, 1089, 1088, 860, 557, 150, 928, 1342, |
| 168156 | /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, |
| 168157 | /* 1720 */ 1251, 445, 1588, 1339, 308, 276, 168, 309, 11, 141, |
| 168158 | /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, |
| 168159 | /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, |
| 168160 | /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, |
| 168161 | /* 1760 */ 223, 1481, 1024, 1480, 1351, 1352, 1350, 1349, 109, 109, |
| 168162 | /* 1770 */ 204, 1591, 1228, 558, 265, 218, 110, 205, 445, 569, |
| 168163 | /* 1780 */ 445, 410, 387, 1014, 1528, 179, 316, 559, 1014, 1014, |
| 168164 | /* 1790 */ 1016, 1017, 27, 230, 1526, 1225, 79, 560, 85, 4, |
| 168165 | /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, |
| 168166 | /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, |
| 168167 | /* 1820 */ 184, 1486, 185, 186, 495, 242, 98, 398, 1408, 36, |
| 168168 | /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1475, 246, |
| 168169 | /* 1840 */ 1497, 490, 346, 277, 248, 196, 493, 511, 557, 350, |
| 168170 | /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, |
| 168171 | /* 1860 */ 1307, 1300, 93, 1605, 881, 1604, 224, 404, 434, 520, |
| 168172 | /* 1870 */ 263, 435, 1574, 563, 1279, 1278, 364, 1024, 306, 1277, |
| 168173 | /* 1880 */ 264, 1603, 1560, 109, 109, 370, 1299, 307, 1559, 438, |
| 168174 | /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, |
| 168175 | /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, |
| 168176 | /* 1910 */ 1183, 530, 272, 274, 379, 210, 1331, 547, 385, 386, |
| 168177 | /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1513, 165, 178, 1514, |
| 168178 | /* 1930 */ 1014, 1014, 1016, 1017, 27, 1512, 1511, 1024, 78, 147, |
| 168179 | /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, |
| 168180 | /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1087, 1014, 1085, |
| 168181 | /* 1960 */ 326, 180, 169, 1208, 182, 334, 238, 913, 241, 1101, |
| 168182 | /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, |
| 168183 | /* 1980 */ 172, 1104, 243, 1100, 244, 158, 18, 245, 345, 247, |
| 168184 | /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1093, 193, 1222, 489, |
| 168185 | /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, |
| 168186 | /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, |
| 168187 | /* 2020 */ 513, 39, 95, 1171, 160, 1053, 964, 1140, 96, 174, |
| 168188 | /* 2030 */ 1139, 225, 280, 282, 198, 958, 113, 1161, 1157, 260, |
| 168189 | /* 2040 */ 21, 22, 23, 1159, 1165, 1164, 1145, 24, 33, 25, |
| 168190 | /* 2050 */ 202, 542, 26, 100, 1068, 102, 1054, 103, 7, 1052, |
| 168191 | /* 2060 */ 1056, 1110, 1057, 1109, 266, 267, 28, 40, 390, 1019, |
| 168192 | /* 2070 */ 861, 112, 29, 564, 1179, 1178, 268, 176, 143, 923, |
| 168193 | /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, |
| 168194 | /* 2090 */ 1238, 1238, 1238, 1238, 269, 1596, |
| 168195 | }; |
| 168196 | static const YYCODETYPE yy_lookahead[] = { |
| 168197 | /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, |
| 168198 | /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, |
| 168199 | /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, |
| @@ -168532,67 +168858,67 @@ | |
| 168532 | /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718, |
| 168533 | /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716, |
| 168534 | /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, |
| 168535 | }; |
| 168536 | static const YYACTIONTYPE yy_default[] = { |
| 168537 | /* 0 */ 1641, 1641, 1641, 1470, 1236, 1347, 1236, 1236, 1236, 1470, |
| 168538 | /* 10 */ 1470, 1470, 1236, 1377, 1377, 1523, 1269, 1236, 1236, 1236, |
| 168539 | /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1469, 1236, 1236, |
| 168540 | /* 30 */ 1236, 1236, 1558, 1558, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168541 | /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, |
| 168542 | /* 50 */ 1471, 1472, 1236, 1236, 1236, 1522, 1524, 1487, 1400, 1399, |
| 168543 | /* 60 */ 1398, 1397, 1505, 1365, 1391, 1384, 1388, 1465, 1466, 1464, |
| 168544 | /* 70 */ 1468, 1472, 1471, 1236, 1387, 1433, 1449, 1432, 1236, 1236, |
| 168545 | /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168546 | /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168547 | /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168548 | /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168549 | /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168550 | /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, |
| 168551 | /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, |
| 168552 | /* 150 */ 1236, 1236, 1236, 1542, 1541, 1236, 1438, 1236, 1269, 1427, |
| 168553 | /* 160 */ 1426, 1452, 1439, 1451, 1450, 1530, 1594, 1593, 1488, 1236, |
| 168554 | /* 170 */ 1236, 1236, 1236, 1236, 1236, 1558, 1236, 1236, 1236, 1236, |
| 168555 | /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168556 | /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, |
| 168557 | /* 200 */ 1558, 1558, 1236, 1269, 1558, 1558, 1368, 1368, 1265, 1265, |
| 168558 | /* 210 */ 1371, 1236, 1537, 1338, 1338, 1338, 1338, 1347, 1338, 1236, |
| 168559 | /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168560 | /* 230 */ 1236, 1236, 1236, 1236, 1527, 1525, 1236, 1236, 1236, 1236, |
| 168561 | /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168562 | /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168563 | /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168564 | /* 270 */ 1236, 1236, 1236, 1236, 1236, 1587, 1236, 1500, 1325, 1343, |
| 168565 | /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633, |
| 168566 | /* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403, |
| 168567 | /* 300 */ 1344, 1630, 1286, 1609, 1281, 1377, 1377, 1377, 1367, 1367, |
| 168568 | /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633, |
| 168569 | /* 320 */ 1353, 1353, 1632, 1632, 1353, 1488, 1617, 1412, 1314, 1320, |
| 168570 | /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1617, 1617, 1390, 1412, |
| 168571 | /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1504, 1627, 1353, 1254, |
| 168572 | /* 350 */ 1478, 1353, 1254, 1353, 1254, 1478, 1312, 1312, 1312, 1301, |
| 168573 | /* 360 */ 1236, 1236, 1478, 1312, 1286, 1312, 1301, 1312, 1312, 1576, |
| 168574 | /* 370 */ 1236, 1482, 1482, 1478, 1353, 1568, 1568, 1380, 1380, 1385, |
| 168575 | /* 380 */ 1371, 1473, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1590, |
| 168576 | /* 390 */ 1590, 1586, 1586, 1586, 1638, 1638, 1537, 1602, 1269, 1269, |
| 168577 | /* 400 */ 1269, 1269, 1602, 1288, 1288, 1270, 1270, 1269, 1602, 1236, |
| 168578 | /* 410 */ 1236, 1236, 1236, 1236, 1236, 1597, 1236, 1532, 1489, 1357, |
| 168579 | /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168580 | /* 430 */ 1236, 1236, 1236, 1236, 1543, 1236, 1236, 1236, 1236, 1236, |
| 168581 | /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1534, 1236, |
| 168582 | /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, |
| 168583 | /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, |
| 168584 | /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168585 | /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1503, 1502, 1236, |
| 168586 | /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168587 | /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, |
| 168588 | /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168589 | /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, |
| 168590 | /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168591 | /* 540 */ 1236, 1236, 1236, 1236, 1573, 1372, 1236, 1236, 1236, 1236, |
| 168592 | /* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168593 | /* 560 */ 1236, 1236, 1236, 1236, 1236, 1613, 1328, 1418, 1236, 1421, |
| 168594 | /* 570 */ 1258, 1236, 1248, 1236, 1236, |
| 168595 | }; |
| 168596 | /********** End of lemon-generated parsing tables *****************************/ |
| 168597 | |
| 168598 | /* The next table maps tokens (terminal symbols) into fallback tokens. |
| @@ -169437,162 +169763,162 @@ | |
| 169437 | /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", |
| 169438 | /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", |
| 169439 | /* 226 */ "case_exprlist ::= WHEN expr THEN expr", |
| 169440 | /* 227 */ "case_else ::= ELSE expr", |
| 169441 | /* 228 */ "case_else ::=", |
| 169442 | /* 229 */ "case_operand ::= expr", |
| 169443 | /* 230 */ "case_operand ::=", |
| 169444 | /* 231 */ "exprlist ::=", |
| 169445 | /* 232 */ "nexprlist ::= nexprlist COMMA expr", |
| 169446 | /* 233 */ "nexprlist ::= expr", |
| 169447 | /* 234 */ "paren_exprlist ::=", |
| 169448 | /* 235 */ "paren_exprlist ::= LP exprlist RP", |
| 169449 | /* 236 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", |
| 169450 | /* 237 */ "uniqueflag ::= UNIQUE", |
| 169451 | /* 238 */ "uniqueflag ::=", |
| 169452 | /* 239 */ "eidlist_opt ::=", |
| 169453 | /* 240 */ "eidlist_opt ::= LP eidlist RP", |
| 169454 | /* 241 */ "eidlist ::= eidlist COMMA nm collate sortorder", |
| 169455 | /* 242 */ "eidlist ::= nm collate sortorder", |
| 169456 | /* 243 */ "collate ::=", |
| 169457 | /* 244 */ "collate ::= COLLATE ID|STRING", |
| 169458 | /* 245 */ "cmd ::= DROP INDEX ifexists fullname", |
| 169459 | /* 246 */ "cmd ::= VACUUM vinto", |
| 169460 | /* 247 */ "cmd ::= VACUUM nm vinto", |
| 169461 | /* 248 */ "vinto ::= INTO expr", |
| 169462 | /* 249 */ "vinto ::=", |
| 169463 | /* 250 */ "cmd ::= PRAGMA nm dbnm", |
| 169464 | /* 251 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", |
| 169465 | /* 252 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", |
| 169466 | /* 253 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", |
| 169467 | /* 254 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", |
| 169468 | /* 255 */ "plus_num ::= PLUS INTEGER|FLOAT", |
| 169469 | /* 256 */ "minus_num ::= MINUS INTEGER|FLOAT", |
| 169470 | /* 257 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", |
| 169471 | /* 258 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", |
| 169472 | /* 259 */ "trigger_time ::= BEFORE|AFTER", |
| 169473 | /* 260 */ "trigger_time ::= INSTEAD OF", |
| 169474 | /* 261 */ "trigger_time ::=", |
| 169475 | /* 262 */ "trigger_event ::= DELETE|INSERT", |
| 169476 | /* 263 */ "trigger_event ::= UPDATE", |
| 169477 | /* 264 */ "trigger_event ::= UPDATE OF idlist", |
| 169478 | /* 265 */ "when_clause ::=", |
| 169479 | /* 266 */ "when_clause ::= WHEN expr", |
| 169480 | /* 267 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", |
| 169481 | /* 268 */ "trigger_cmd_list ::= trigger_cmd SEMI", |
| 169482 | /* 269 */ "trnm ::= nm DOT nm", |
| 169483 | /* 270 */ "tridxby ::= INDEXED BY nm", |
| 169484 | /* 271 */ "tridxby ::= NOT INDEXED", |
| 169485 | /* 272 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", |
| 169486 | /* 273 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", |
| 169487 | /* 274 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", |
| 169488 | /* 275 */ "trigger_cmd ::= scanpt select scanpt", |
| 169489 | /* 276 */ "expr ::= RAISE LP IGNORE RP", |
| 169490 | /* 277 */ "expr ::= RAISE LP raisetype COMMA nm RP", |
| 169491 | /* 278 */ "raisetype ::= ROLLBACK", |
| 169492 | /* 279 */ "raisetype ::= ABORT", |
| 169493 | /* 280 */ "raisetype ::= FAIL", |
| 169494 | /* 281 */ "cmd ::= DROP TRIGGER ifexists fullname", |
| 169495 | /* 282 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", |
| 169496 | /* 283 */ "cmd ::= DETACH database_kw_opt expr", |
| 169497 | /* 284 */ "key_opt ::=", |
| 169498 | /* 285 */ "key_opt ::= KEY expr", |
| 169499 | /* 286 */ "cmd ::= REINDEX", |
| 169500 | /* 287 */ "cmd ::= REINDEX nm dbnm", |
| 169501 | /* 288 */ "cmd ::= ANALYZE", |
| 169502 | /* 289 */ "cmd ::= ANALYZE nm dbnm", |
| 169503 | /* 290 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", |
| 169504 | /* 291 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", |
| 169505 | /* 292 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", |
| 169506 | /* 293 */ "add_column_fullname ::= fullname", |
| 169507 | /* 294 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", |
| 169508 | /* 295 */ "cmd ::= create_vtab", |
| 169509 | /* 296 */ "cmd ::= create_vtab LP vtabarglist RP", |
| 169510 | /* 297 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", |
| 169511 | /* 298 */ "vtabarg ::=", |
| 169512 | /* 299 */ "vtabargtoken ::= ANY", |
| 169513 | /* 300 */ "vtabargtoken ::= lp anylist RP", |
| 169514 | /* 301 */ "lp ::= LP", |
| 169515 | /* 302 */ "with ::= WITH wqlist", |
| 169516 | /* 303 */ "with ::= WITH RECURSIVE wqlist", |
| 169517 | /* 304 */ "wqas ::= AS", |
| 169518 | /* 305 */ "wqas ::= AS MATERIALIZED", |
| 169519 | /* 306 */ "wqas ::= AS NOT MATERIALIZED", |
| 169520 | /* 307 */ "wqitem ::= nm eidlist_opt wqas LP select RP", |
| 169521 | /* 308 */ "wqlist ::= wqitem", |
| 169522 | /* 309 */ "wqlist ::= wqlist COMMA wqitem", |
| 169523 | /* 310 */ "windowdefn_list ::= windowdefn", |
| 169524 | /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", |
| 169525 | /* 312 */ "windowdefn ::= nm AS LP window RP", |
| 169526 | /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", |
| 169527 | /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", |
| 169528 | /* 315 */ "window ::= ORDER BY sortlist frame_opt", |
| 169529 | /* 316 */ "window ::= nm ORDER BY sortlist frame_opt", |
| 169530 | /* 317 */ "window ::= frame_opt", |
| 169531 | /* 318 */ "window ::= nm frame_opt", |
| 169532 | /* 319 */ "frame_opt ::=", |
| 169533 | /* 320 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", |
| 169534 | /* 321 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", |
| 169535 | /* 322 */ "range_or_rows ::= RANGE|ROWS|GROUPS", |
| 169536 | /* 323 */ "frame_bound_s ::= frame_bound", |
| 169537 | /* 324 */ "frame_bound_s ::= UNBOUNDED PRECEDING", |
| 169538 | /* 325 */ "frame_bound_e ::= frame_bound", |
| 169539 | /* 326 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", |
| 169540 | /* 327 */ "frame_bound ::= expr PRECEDING|FOLLOWING", |
| 169541 | /* 328 */ "frame_bound ::= CURRENT ROW", |
| 169542 | /* 329 */ "frame_exclude_opt ::=", |
| 169543 | /* 330 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", |
| 169544 | /* 331 */ "frame_exclude ::= NO OTHERS", |
| 169545 | /* 332 */ "frame_exclude ::= CURRENT ROW", |
| 169546 | /* 333 */ "frame_exclude ::= GROUP|TIES", |
| 169547 | /* 334 */ "window_clause ::= WINDOW windowdefn_list", |
| 169548 | /* 335 */ "filter_over ::= filter_clause over_clause", |
| 169549 | /* 336 */ "filter_over ::= over_clause", |
| 169550 | /* 337 */ "filter_over ::= filter_clause", |
| 169551 | /* 338 */ "over_clause ::= OVER LP window RP", |
| 169552 | /* 339 */ "over_clause ::= OVER nm", |
| 169553 | /* 340 */ "filter_clause ::= FILTER LP WHERE expr RP", |
| 169554 | /* 341 */ "input ::= cmdlist", |
| 169555 | /* 342 */ "cmdlist ::= cmdlist ecmd", |
| 169556 | /* 343 */ "cmdlist ::= ecmd", |
| 169557 | /* 344 */ "ecmd ::= SEMI", |
| 169558 | /* 345 */ "ecmd ::= cmdx SEMI", |
| 169559 | /* 346 */ "ecmd ::= explain cmdx SEMI", |
| 169560 | /* 347 */ "trans_opt ::=", |
| 169561 | /* 348 */ "trans_opt ::= TRANSACTION", |
| 169562 | /* 349 */ "trans_opt ::= TRANSACTION nm", |
| 169563 | /* 350 */ "savepoint_opt ::= SAVEPOINT", |
| 169564 | /* 351 */ "savepoint_opt ::=", |
| 169565 | /* 352 */ "cmd ::= create_table create_table_args", |
| 169566 | /* 353 */ "table_option_set ::= table_option", |
| 169567 | /* 354 */ "columnlist ::= columnlist COMMA columnname carglist", |
| 169568 | /* 355 */ "columnlist ::= columnname carglist", |
| 169569 | /* 356 */ "nm ::= ID|INDEXED|JOIN_KW", |
| 169570 | /* 357 */ "nm ::= STRING", |
| 169571 | /* 358 */ "typetoken ::= typename", |
| 169572 | /* 359 */ "typename ::= ID|STRING", |
| 169573 | /* 360 */ "signed ::= plus_num", |
| 169574 | /* 361 */ "signed ::= minus_num", |
| 169575 | /* 362 */ "carglist ::= carglist ccons", |
| 169576 | /* 363 */ "carglist ::=", |
| 169577 | /* 364 */ "ccons ::= NULL onconf", |
| 169578 | /* 365 */ "ccons ::= GENERATED ALWAYS AS generated", |
| 169579 | /* 366 */ "ccons ::= AS generated", |
| 169580 | /* 367 */ "conslist_opt ::= COMMA conslist", |
| 169581 | /* 368 */ "conslist ::= conslist tconscomma tcons", |
| 169582 | /* 369 */ "conslist ::= tcons", |
| 169583 | /* 370 */ "tconscomma ::=", |
| 169584 | /* 371 */ "defer_subclause_opt ::= defer_subclause", |
| 169585 | /* 372 */ "resolvetype ::= raisetype", |
| 169586 | /* 373 */ "selectnowith ::= oneselect", |
| 169587 | /* 374 */ "oneselect ::= values", |
| 169588 | /* 375 */ "sclp ::= selcollist COMMA", |
| 169589 | /* 376 */ "as ::= ID|STRING", |
| 169590 | /* 377 */ "indexed_opt ::= indexed_by", |
| 169591 | /* 378 */ "returning ::=", |
| 169592 | /* 379 */ "expr ::= term", |
| 169593 | /* 380 */ "likeop ::= LIKE_KW|MATCH", |
| 169594 | /* 381 */ "exprlist ::= nexprlist", |
| 169595 | /* 382 */ "nmnum ::= plus_num", |
| 169596 | /* 383 */ "nmnum ::= nm", |
| 169597 | /* 384 */ "nmnum ::= ON", |
| 169598 | /* 385 */ "nmnum ::= DELETE", |
| @@ -170346,162 +170672,162 @@ | |
| 170346 | 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 170347 | 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 170348 | 279, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 170349 | 280, /* (227) case_else ::= ELSE expr */ |
| 170350 | 280, /* (228) case_else ::= */ |
| 170351 | 278, /* (229) case_operand ::= expr */ |
| 170352 | 278, /* (230) case_operand ::= */ |
| 170353 | 261, /* (231) exprlist ::= */ |
| 170354 | 253, /* (232) nexprlist ::= nexprlist COMMA expr */ |
| 170355 | 253, /* (233) nexprlist ::= expr */ |
| 170356 | 277, /* (234) paren_exprlist ::= */ |
| 170357 | 277, /* (235) paren_exprlist ::= LP exprlist RP */ |
| 170358 | 190, /* (236) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 170359 | 281, /* (237) uniqueflag ::= UNIQUE */ |
| 170360 | 281, /* (238) uniqueflag ::= */ |
| 170361 | 221, /* (239) eidlist_opt ::= */ |
| 170362 | 221, /* (240) eidlist_opt ::= LP eidlist RP */ |
| 170363 | 232, /* (241) eidlist ::= eidlist COMMA nm collate sortorder */ |
| 170364 | 232, /* (242) eidlist ::= nm collate sortorder */ |
| 170365 | 282, /* (243) collate ::= */ |
| 170366 | 282, /* (244) collate ::= COLLATE ID|STRING */ |
| 170367 | 190, /* (245) cmd ::= DROP INDEX ifexists fullname */ |
| 170368 | 190, /* (246) cmd ::= VACUUM vinto */ |
| 170369 | 190, /* (247) cmd ::= VACUUM nm vinto */ |
| 170370 | 283, /* (248) vinto ::= INTO expr */ |
| 170371 | 283, /* (249) vinto ::= */ |
| 170372 | 190, /* (250) cmd ::= PRAGMA nm dbnm */ |
| 170373 | 190, /* (251) cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 170374 | 190, /* (252) cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 170375 | 190, /* (253) cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 170376 | 190, /* (254) cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 170377 | 211, /* (255) plus_num ::= PLUS INTEGER|FLOAT */ |
| 170378 | 212, /* (256) minus_num ::= MINUS INTEGER|FLOAT */ |
| 170379 | 190, /* (257) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 170380 | 285, /* (258) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 170381 | 287, /* (259) trigger_time ::= BEFORE|AFTER */ |
| 170382 | 287, /* (260) trigger_time ::= INSTEAD OF */ |
| 170383 | 287, /* (261) trigger_time ::= */ |
| 170384 | 288, /* (262) trigger_event ::= DELETE|INSERT */ |
| 170385 | 288, /* (263) trigger_event ::= UPDATE */ |
| 170386 | 288, /* (264) trigger_event ::= UPDATE OF idlist */ |
| 170387 | 290, /* (265) when_clause ::= */ |
| 170388 | 290, /* (266) when_clause ::= WHEN expr */ |
| 170389 | 286, /* (267) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 170390 | 286, /* (268) trigger_cmd_list ::= trigger_cmd SEMI */ |
| 170391 | 292, /* (269) trnm ::= nm DOT nm */ |
| 170392 | 293, /* (270) tridxby ::= INDEXED BY nm */ |
| 170393 | 293, /* (271) tridxby ::= NOT INDEXED */ |
| 170394 | 291, /* (272) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 170395 | 291, /* (273) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 170396 | 291, /* (274) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 170397 | 291, /* (275) trigger_cmd ::= scanpt select scanpt */ |
| 170398 | 217, /* (276) expr ::= RAISE LP IGNORE RP */ |
| 170399 | 217, /* (277) expr ::= RAISE LP raisetype COMMA nm RP */ |
| 170400 | 236, /* (278) raisetype ::= ROLLBACK */ |
| 170401 | 236, /* (279) raisetype ::= ABORT */ |
| 170402 | 236, /* (280) raisetype ::= FAIL */ |
| 170403 | 190, /* (281) cmd ::= DROP TRIGGER ifexists fullname */ |
| 170404 | 190, /* (282) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 170405 | 190, /* (283) cmd ::= DETACH database_kw_opt expr */ |
| 170406 | 295, /* (284) key_opt ::= */ |
| 170407 | 295, /* (285) key_opt ::= KEY expr */ |
| 170408 | 190, /* (286) cmd ::= REINDEX */ |
| 170409 | 190, /* (287) cmd ::= REINDEX nm dbnm */ |
| 170410 | 190, /* (288) cmd ::= ANALYZE */ |
| 170411 | 190, /* (289) cmd ::= ANALYZE nm dbnm */ |
| 170412 | 190, /* (290) cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 170413 | 190, /* (291) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 170414 | 190, /* (292) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 170415 | 296, /* (293) add_column_fullname ::= fullname */ |
| 170416 | 190, /* (294) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 170417 | 190, /* (295) cmd ::= create_vtab */ |
| 170418 | 190, /* (296) cmd ::= create_vtab LP vtabarglist RP */ |
| 170419 | 298, /* (297) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 170420 | 300, /* (298) vtabarg ::= */ |
| 170421 | 301, /* (299) vtabargtoken ::= ANY */ |
| 170422 | 301, /* (300) vtabargtoken ::= lp anylist RP */ |
| 170423 | 302, /* (301) lp ::= LP */ |
| 170424 | 266, /* (302) with ::= WITH wqlist */ |
| 170425 | 266, /* (303) with ::= WITH RECURSIVE wqlist */ |
| 170426 | 305, /* (304) wqas ::= AS */ |
| 170427 | 305, /* (305) wqas ::= AS MATERIALIZED */ |
| 170428 | 305, /* (306) wqas ::= AS NOT MATERIALIZED */ |
| 170429 | 304, /* (307) wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 170430 | 241, /* (308) wqlist ::= wqitem */ |
| 170431 | 241, /* (309) wqlist ::= wqlist COMMA wqitem */ |
| 170432 | 306, /* (310) windowdefn_list ::= windowdefn */ |
| 170433 | 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 170434 | 307, /* (312) windowdefn ::= nm AS LP window RP */ |
| 170435 | 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170436 | 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170437 | 308, /* (315) window ::= ORDER BY sortlist frame_opt */ |
| 170438 | 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */ |
| 170439 | 308, /* (317) window ::= frame_opt */ |
| 170440 | 308, /* (318) window ::= nm frame_opt */ |
| 170441 | 309, /* (319) frame_opt ::= */ |
| 170442 | 309, /* (320) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 170443 | 309, /* (321) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 170444 | 313, /* (322) range_or_rows ::= RANGE|ROWS|GROUPS */ |
| 170445 | 315, /* (323) frame_bound_s ::= frame_bound */ |
| 170446 | 315, /* (324) frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 170447 | 316, /* (325) frame_bound_e ::= frame_bound */ |
| 170448 | 316, /* (326) frame_bound_e ::= UNBOUNDED FOLLOWING */ |
| 170449 | 314, /* (327) frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 170450 | 314, /* (328) frame_bound ::= CURRENT ROW */ |
| 170451 | 317, /* (329) frame_exclude_opt ::= */ |
| 170452 | 317, /* (330) frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 170453 | 318, /* (331) frame_exclude ::= NO OTHERS */ |
| 170454 | 318, /* (332) frame_exclude ::= CURRENT ROW */ |
| 170455 | 318, /* (333) frame_exclude ::= GROUP|TIES */ |
| 170456 | 251, /* (334) window_clause ::= WINDOW windowdefn_list */ |
| 170457 | 273, /* (335) filter_over ::= filter_clause over_clause */ |
| 170458 | 273, /* (336) filter_over ::= over_clause */ |
| 170459 | 273, /* (337) filter_over ::= filter_clause */ |
| 170460 | 312, /* (338) over_clause ::= OVER LP window RP */ |
| 170461 | 312, /* (339) over_clause ::= OVER nm */ |
| 170462 | 311, /* (340) filter_clause ::= FILTER LP WHERE expr RP */ |
| 170463 | 185, /* (341) input ::= cmdlist */ |
| 170464 | 186, /* (342) cmdlist ::= cmdlist ecmd */ |
| 170465 | 186, /* (343) cmdlist ::= ecmd */ |
| 170466 | 187, /* (344) ecmd ::= SEMI */ |
| 170467 | 187, /* (345) ecmd ::= cmdx SEMI */ |
| 170468 | 187, /* (346) ecmd ::= explain cmdx SEMI */ |
| 170469 | 192, /* (347) trans_opt ::= */ |
| 170470 | 192, /* (348) trans_opt ::= TRANSACTION */ |
| 170471 | 192, /* (349) trans_opt ::= TRANSACTION nm */ |
| 170472 | 194, /* (350) savepoint_opt ::= SAVEPOINT */ |
| 170473 | 194, /* (351) savepoint_opt ::= */ |
| 170474 | 190, /* (352) cmd ::= create_table create_table_args */ |
| 170475 | 203, /* (353) table_option_set ::= table_option */ |
| 170476 | 201, /* (354) columnlist ::= columnlist COMMA columnname carglist */ |
| 170477 | 201, /* (355) columnlist ::= columnname carglist */ |
| 170478 | 193, /* (356) nm ::= ID|INDEXED|JOIN_KW */ |
| 170479 | 193, /* (357) nm ::= STRING */ |
| 170480 | 208, /* (358) typetoken ::= typename */ |
| 170481 | 209, /* (359) typename ::= ID|STRING */ |
| 170482 | 210, /* (360) signed ::= plus_num */ |
| 170483 | 210, /* (361) signed ::= minus_num */ |
| 170484 | 207, /* (362) carglist ::= carglist ccons */ |
| 170485 | 207, /* (363) carglist ::= */ |
| 170486 | 215, /* (364) ccons ::= NULL onconf */ |
| 170487 | 215, /* (365) ccons ::= GENERATED ALWAYS AS generated */ |
| 170488 | 215, /* (366) ccons ::= AS generated */ |
| 170489 | 202, /* (367) conslist_opt ::= COMMA conslist */ |
| 170490 | 228, /* (368) conslist ::= conslist tconscomma tcons */ |
| 170491 | 228, /* (369) conslist ::= tcons */ |
| 170492 | 229, /* (370) tconscomma ::= */ |
| 170493 | 233, /* (371) defer_subclause_opt ::= defer_subclause */ |
| 170494 | 235, /* (372) resolvetype ::= raisetype */ |
| 170495 | 239, /* (373) selectnowith ::= oneselect */ |
| 170496 | 240, /* (374) oneselect ::= values */ |
| 170497 | 254, /* (375) sclp ::= selcollist COMMA */ |
| 170498 | 255, /* (376) as ::= ID|STRING */ |
| 170499 | 264, /* (377) indexed_opt ::= indexed_by */ |
| 170500 | 272, /* (378) returning ::= */ |
| 170501 | 217, /* (379) expr ::= term */ |
| 170502 | 274, /* (380) likeop ::= LIKE_KW|MATCH */ |
| 170503 | 261, /* (381) exprlist ::= nexprlist */ |
| 170504 | 284, /* (382) nmnum ::= plus_num */ |
| 170505 | 284, /* (383) nmnum ::= nm */ |
| 170506 | 284, /* (384) nmnum ::= ON */ |
| 170507 | 284, /* (385) nmnum ::= DELETE */ |
| @@ -170754,162 +171080,162 @@ | |
| 170754 | -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 170755 | -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 170756 | -4, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 170757 | -2, /* (227) case_else ::= ELSE expr */ |
| 170758 | 0, /* (228) case_else ::= */ |
| 170759 | -1, /* (229) case_operand ::= expr */ |
| 170760 | 0, /* (230) case_operand ::= */ |
| 170761 | 0, /* (231) exprlist ::= */ |
| 170762 | -3, /* (232) nexprlist ::= nexprlist COMMA expr */ |
| 170763 | -1, /* (233) nexprlist ::= expr */ |
| 170764 | 0, /* (234) paren_exprlist ::= */ |
| 170765 | -3, /* (235) paren_exprlist ::= LP exprlist RP */ |
| 170766 | -12, /* (236) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 170767 | -1, /* (237) uniqueflag ::= UNIQUE */ |
| 170768 | 0, /* (238) uniqueflag ::= */ |
| 170769 | 0, /* (239) eidlist_opt ::= */ |
| 170770 | -3, /* (240) eidlist_opt ::= LP eidlist RP */ |
| 170771 | -5, /* (241) eidlist ::= eidlist COMMA nm collate sortorder */ |
| 170772 | -3, /* (242) eidlist ::= nm collate sortorder */ |
| 170773 | 0, /* (243) collate ::= */ |
| 170774 | -2, /* (244) collate ::= COLLATE ID|STRING */ |
| 170775 | -4, /* (245) cmd ::= DROP INDEX ifexists fullname */ |
| 170776 | -2, /* (246) cmd ::= VACUUM vinto */ |
| 170777 | -3, /* (247) cmd ::= VACUUM nm vinto */ |
| 170778 | -2, /* (248) vinto ::= INTO expr */ |
| 170779 | 0, /* (249) vinto ::= */ |
| 170780 | -3, /* (250) cmd ::= PRAGMA nm dbnm */ |
| 170781 | -5, /* (251) cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 170782 | -6, /* (252) cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 170783 | -5, /* (253) cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 170784 | -6, /* (254) cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 170785 | -2, /* (255) plus_num ::= PLUS INTEGER|FLOAT */ |
| 170786 | -2, /* (256) minus_num ::= MINUS INTEGER|FLOAT */ |
| 170787 | -5, /* (257) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 170788 | -11, /* (258) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 170789 | -1, /* (259) trigger_time ::= BEFORE|AFTER */ |
| 170790 | -2, /* (260) trigger_time ::= INSTEAD OF */ |
| 170791 | 0, /* (261) trigger_time ::= */ |
| 170792 | -1, /* (262) trigger_event ::= DELETE|INSERT */ |
| 170793 | -1, /* (263) trigger_event ::= UPDATE */ |
| 170794 | -3, /* (264) trigger_event ::= UPDATE OF idlist */ |
| 170795 | 0, /* (265) when_clause ::= */ |
| 170796 | -2, /* (266) when_clause ::= WHEN expr */ |
| 170797 | -3, /* (267) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 170798 | -2, /* (268) trigger_cmd_list ::= trigger_cmd SEMI */ |
| 170799 | -3, /* (269) trnm ::= nm DOT nm */ |
| 170800 | -3, /* (270) tridxby ::= INDEXED BY nm */ |
| 170801 | -2, /* (271) tridxby ::= NOT INDEXED */ |
| 170802 | -9, /* (272) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 170803 | -8, /* (273) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 170804 | -6, /* (274) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 170805 | -3, /* (275) trigger_cmd ::= scanpt select scanpt */ |
| 170806 | -4, /* (276) expr ::= RAISE LP IGNORE RP */ |
| 170807 | -6, /* (277) expr ::= RAISE LP raisetype COMMA nm RP */ |
| 170808 | -1, /* (278) raisetype ::= ROLLBACK */ |
| 170809 | -1, /* (279) raisetype ::= ABORT */ |
| 170810 | -1, /* (280) raisetype ::= FAIL */ |
| 170811 | -4, /* (281) cmd ::= DROP TRIGGER ifexists fullname */ |
| 170812 | -6, /* (282) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 170813 | -3, /* (283) cmd ::= DETACH database_kw_opt expr */ |
| 170814 | 0, /* (284) key_opt ::= */ |
| 170815 | -2, /* (285) key_opt ::= KEY expr */ |
| 170816 | -1, /* (286) cmd ::= REINDEX */ |
| 170817 | -3, /* (287) cmd ::= REINDEX nm dbnm */ |
| 170818 | -1, /* (288) cmd ::= ANALYZE */ |
| 170819 | -3, /* (289) cmd ::= ANALYZE nm dbnm */ |
| 170820 | -6, /* (290) cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 170821 | -7, /* (291) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 170822 | -6, /* (292) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 170823 | -1, /* (293) add_column_fullname ::= fullname */ |
| 170824 | -8, /* (294) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 170825 | -1, /* (295) cmd ::= create_vtab */ |
| 170826 | -4, /* (296) cmd ::= create_vtab LP vtabarglist RP */ |
| 170827 | -8, /* (297) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 170828 | 0, /* (298) vtabarg ::= */ |
| 170829 | -1, /* (299) vtabargtoken ::= ANY */ |
| 170830 | -3, /* (300) vtabargtoken ::= lp anylist RP */ |
| 170831 | -1, /* (301) lp ::= LP */ |
| 170832 | -2, /* (302) with ::= WITH wqlist */ |
| 170833 | -3, /* (303) with ::= WITH RECURSIVE wqlist */ |
| 170834 | -1, /* (304) wqas ::= AS */ |
| 170835 | -2, /* (305) wqas ::= AS MATERIALIZED */ |
| 170836 | -3, /* (306) wqas ::= AS NOT MATERIALIZED */ |
| 170837 | -6, /* (307) wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 170838 | -1, /* (308) wqlist ::= wqitem */ |
| 170839 | -3, /* (309) wqlist ::= wqlist COMMA wqitem */ |
| 170840 | -1, /* (310) windowdefn_list ::= windowdefn */ |
| 170841 | -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 170842 | -5, /* (312) windowdefn ::= nm AS LP window RP */ |
| 170843 | -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170844 | -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170845 | -4, /* (315) window ::= ORDER BY sortlist frame_opt */ |
| 170846 | -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */ |
| 170847 | -1, /* (317) window ::= frame_opt */ |
| 170848 | -2, /* (318) window ::= nm frame_opt */ |
| 170849 | 0, /* (319) frame_opt ::= */ |
| 170850 | -3, /* (320) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 170851 | -6, /* (321) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 170852 | -1, /* (322) range_or_rows ::= RANGE|ROWS|GROUPS */ |
| 170853 | -1, /* (323) frame_bound_s ::= frame_bound */ |
| 170854 | -2, /* (324) frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 170855 | -1, /* (325) frame_bound_e ::= frame_bound */ |
| 170856 | -2, /* (326) frame_bound_e ::= UNBOUNDED FOLLOWING */ |
| 170857 | -2, /* (327) frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 170858 | -2, /* (328) frame_bound ::= CURRENT ROW */ |
| 170859 | 0, /* (329) frame_exclude_opt ::= */ |
| 170860 | -2, /* (330) frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 170861 | -2, /* (331) frame_exclude ::= NO OTHERS */ |
| 170862 | -2, /* (332) frame_exclude ::= CURRENT ROW */ |
| 170863 | -1, /* (333) frame_exclude ::= GROUP|TIES */ |
| 170864 | -2, /* (334) window_clause ::= WINDOW windowdefn_list */ |
| 170865 | -2, /* (335) filter_over ::= filter_clause over_clause */ |
| 170866 | -1, /* (336) filter_over ::= over_clause */ |
| 170867 | -1, /* (337) filter_over ::= filter_clause */ |
| 170868 | -4, /* (338) over_clause ::= OVER LP window RP */ |
| 170869 | -2, /* (339) over_clause ::= OVER nm */ |
| 170870 | -5, /* (340) filter_clause ::= FILTER LP WHERE expr RP */ |
| 170871 | -1, /* (341) input ::= cmdlist */ |
| 170872 | -2, /* (342) cmdlist ::= cmdlist ecmd */ |
| 170873 | -1, /* (343) cmdlist ::= ecmd */ |
| 170874 | -1, /* (344) ecmd ::= SEMI */ |
| 170875 | -2, /* (345) ecmd ::= cmdx SEMI */ |
| 170876 | -3, /* (346) ecmd ::= explain cmdx SEMI */ |
| 170877 | 0, /* (347) trans_opt ::= */ |
| 170878 | -1, /* (348) trans_opt ::= TRANSACTION */ |
| 170879 | -2, /* (349) trans_opt ::= TRANSACTION nm */ |
| 170880 | -1, /* (350) savepoint_opt ::= SAVEPOINT */ |
| 170881 | 0, /* (351) savepoint_opt ::= */ |
| 170882 | -2, /* (352) cmd ::= create_table create_table_args */ |
| 170883 | -1, /* (353) table_option_set ::= table_option */ |
| 170884 | -4, /* (354) columnlist ::= columnlist COMMA columnname carglist */ |
| 170885 | -2, /* (355) columnlist ::= columnname carglist */ |
| 170886 | -1, /* (356) nm ::= ID|INDEXED|JOIN_KW */ |
| 170887 | -1, /* (357) nm ::= STRING */ |
| 170888 | -1, /* (358) typetoken ::= typename */ |
| 170889 | -1, /* (359) typename ::= ID|STRING */ |
| 170890 | -1, /* (360) signed ::= plus_num */ |
| 170891 | -1, /* (361) signed ::= minus_num */ |
| 170892 | -2, /* (362) carglist ::= carglist ccons */ |
| 170893 | 0, /* (363) carglist ::= */ |
| 170894 | -2, /* (364) ccons ::= NULL onconf */ |
| 170895 | -4, /* (365) ccons ::= GENERATED ALWAYS AS generated */ |
| 170896 | -2, /* (366) ccons ::= AS generated */ |
| 170897 | -2, /* (367) conslist_opt ::= COMMA conslist */ |
| 170898 | -3, /* (368) conslist ::= conslist tconscomma tcons */ |
| 170899 | -1, /* (369) conslist ::= tcons */ |
| 170900 | 0, /* (370) tconscomma ::= */ |
| 170901 | -1, /* (371) defer_subclause_opt ::= defer_subclause */ |
| 170902 | -1, /* (372) resolvetype ::= raisetype */ |
| 170903 | -1, /* (373) selectnowith ::= oneselect */ |
| 170904 | -1, /* (374) oneselect ::= values */ |
| 170905 | -2, /* (375) sclp ::= selcollist COMMA */ |
| 170906 | -1, /* (376) as ::= ID|STRING */ |
| 170907 | -1, /* (377) indexed_opt ::= indexed_by */ |
| 170908 | 0, /* (378) returning ::= */ |
| 170909 | -1, /* (379) expr ::= term */ |
| 170910 | -1, /* (380) likeop ::= LIKE_KW|MATCH */ |
| 170911 | -1, /* (381) exprlist ::= nexprlist */ |
| 170912 | -1, /* (382) nmnum ::= plus_num */ |
| 170913 | -1, /* (383) nmnum ::= nm */ |
| 170914 | -1, /* (384) nmnum ::= ON */ |
| 170915 | -1, /* (385) nmnum ::= DELETE */ |
| @@ -170987,11 +171313,11 @@ | |
| 170987 | {yymsp[1].minor.yy394 = TK_DEFERRED;} |
| 170988 | break; |
| 170989 | case 5: /* transtype ::= DEFERRED */ |
| 170990 | case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); |
| 170991 | case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); |
| 170992 | case 322: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==322); |
| 170993 | {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} |
| 170994 | break; |
| 170995 | case 8: /* cmd ::= COMMIT|END trans_opt */ |
| 170996 | case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); |
| 170997 | {sqlite3EndTransaction(pParse,yymsp[-1].major);} |
| @@ -171024,11 +171350,11 @@ | |
| 171024 | case 47: /* autoinc ::= */ yytestcase(yyruleno==47); |
| 171025 | case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); |
| 171026 | case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); |
| 171027 | case 81: /* ifexists ::= */ yytestcase(yyruleno==81); |
| 171028 | case 98: /* distinct ::= */ yytestcase(yyruleno==98); |
| 171029 | case 243: /* collate ::= */ yytestcase(yyruleno==243); |
| 171030 | {yymsp[1].minor.yy394 = 0;} |
| 171031 | break; |
| 171032 | case 16: /* ifnotexists ::= IF NOT EXISTS */ |
| 171033 | {yymsp[-2].minor.yy394 = 1;} |
| 171034 | break; |
| @@ -171210,11 +171536,11 @@ | |
| 171210 | break; |
| 171211 | case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ |
| 171212 | case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); |
| 171213 | case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215); |
| 171214 | case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218); |
| 171215 | case 244: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==244); |
| 171216 | {yymsp[-1].minor.yy394 = 1;} |
| 171217 | break; |
| 171218 | case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ |
| 171219 | {yymsp[-1].minor.yy394 = 0;} |
| 171220 | break; |
| @@ -171360,13 +171686,13 @@ | |
| 171360 | {yymsp[0].minor.yy394 = SF_All;} |
| 171361 | break; |
| 171362 | case 99: /* sclp ::= */ |
| 171363 | case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); |
| 171364 | case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); |
| 171365 | case 231: /* exprlist ::= */ yytestcase(yyruleno==231); |
| 171366 | case 234: /* paren_exprlist ::= */ yytestcase(yyruleno==234); |
| 171367 | case 239: /* eidlist_opt ::= */ yytestcase(yyruleno==239); |
| 171368 | {yymsp[1].minor.yy322 = 0;} |
| 171369 | break; |
| 171370 | case 100: /* selcollist ::= sclp scanpt expr scanpt as */ |
| 171371 | { |
| 171372 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); |
| @@ -171388,12 +171714,12 @@ | |
| 171388 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); |
| 171389 | } |
| 171390 | break; |
| 171391 | case 103: /* as ::= AS nm */ |
| 171392 | case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); |
| 171393 | case 255: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==255); |
| 171394 | case 256: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==256); |
| 171395 | {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} |
| 171396 | break; |
| 171397 | case 105: /* from ::= */ |
| 171398 | case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); |
| 171399 | {yymsp[1].minor.yy131 = 0;} |
| @@ -171433,11 +171759,11 @@ | |
| 171433 | break; |
| 171434 | case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ |
| 171435 | { |
| 171436 | if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ |
| 171437 | yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; |
| 171438 | }else if( yymsp[-3].minor.yy131->nSrc==1 ){ |
| 171439 | yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); |
| 171440 | if( yymsp[-5].minor.yy131 ){ |
| 171441 | SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; |
| 171442 | SrcItem *pOld = yymsp[-3].minor.yy131->a; |
| 171443 | pNew->zName = pOld->zName; |
| @@ -171562,19 +171888,19 @@ | |
| 171562 | case 144: /* having_opt ::= */ |
| 171563 | case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); |
| 171564 | case 151: /* where_opt ::= */ yytestcase(yyruleno==151); |
| 171565 | case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); |
| 171566 | case 228: /* case_else ::= */ yytestcase(yyruleno==228); |
| 171567 | case 230: /* case_operand ::= */ yytestcase(yyruleno==230); |
| 171568 | case 249: /* vinto ::= */ yytestcase(yyruleno==249); |
| 171569 | {yymsp[1].minor.yy528 = 0;} |
| 171570 | break; |
| 171571 | case 145: /* having_opt ::= HAVING expr */ |
| 171572 | case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); |
| 171573 | case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); |
| 171574 | case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227); |
| 171575 | case 248: /* vinto ::= INTO expr */ yytestcase(yyruleno==248); |
| 171576 | {yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} |
| 171577 | break; |
| 171578 | case 147: /* limit_opt ::= LIMIT expr */ |
| 171579 | {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} |
| 171580 | break; |
| @@ -172001,394 +172327,391 @@ | |
| 172001 | { |
| 172002 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); |
| 172003 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); |
| 172004 | } |
| 172005 | break; |
| 172006 | case 229: /* case_operand ::= expr */ |
| 172007 | {yymsp[0].minor.yy528 = yymsp[0].minor.yy528; /*A-overwrites-X*/} |
| 172008 | break; |
| 172009 | case 232: /* nexprlist ::= nexprlist COMMA expr */ |
| 172010 | {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} |
| 172011 | break; |
| 172012 | case 233: /* nexprlist ::= expr */ |
| 172013 | {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} |
| 172014 | break; |
| 172015 | case 235: /* paren_exprlist ::= LP exprlist RP */ |
| 172016 | case 240: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==240); |
| 172017 | {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} |
| 172018 | break; |
| 172019 | case 236: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 172020 | { |
| 172021 | sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, |
| 172022 | sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, |
| 172023 | &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); |
| 172024 | if( IN_RENAME_OBJECT && pParse->pNewIndex ){ |
| 172025 | sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); |
| 172026 | } |
| 172027 | } |
| 172028 | break; |
| 172029 | case 237: /* uniqueflag ::= UNIQUE */ |
| 172030 | case 279: /* raisetype ::= ABORT */ yytestcase(yyruleno==279); |
| 172031 | {yymsp[0].minor.yy394 = OE_Abort;} |
| 172032 | break; |
| 172033 | case 238: /* uniqueflag ::= */ |
| 172034 | {yymsp[1].minor.yy394 = OE_None;} |
| 172035 | break; |
| 172036 | case 241: /* eidlist ::= eidlist COMMA nm collate sortorder */ |
| 172037 | { |
| 172038 | yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); |
| 172039 | } |
| 172040 | break; |
| 172041 | case 242: /* eidlist ::= nm collate sortorder */ |
| 172042 | { |
| 172043 | yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ |
| 172044 | } |
| 172045 | break; |
| 172046 | case 245: /* cmd ::= DROP INDEX ifexists fullname */ |
| 172047 | {sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} |
| 172048 | break; |
| 172049 | case 246: /* cmd ::= VACUUM vinto */ |
| 172050 | {sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} |
| 172051 | break; |
| 172052 | case 247: /* cmd ::= VACUUM nm vinto */ |
| 172053 | {sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} |
| 172054 | break; |
| 172055 | case 250: /* cmd ::= PRAGMA nm dbnm */ |
| 172056 | {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} |
| 172057 | break; |
| 172058 | case 251: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 172059 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} |
| 172060 | break; |
| 172061 | case 252: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 172062 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} |
| 172063 | break; |
| 172064 | case 253: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 172065 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} |
| 172066 | break; |
| 172067 | case 254: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 172068 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} |
| 172069 | break; |
| 172070 | case 257: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 172071 | { |
| 172072 | Token all; |
| 172073 | all.z = yymsp[-3].minor.yy0.z; |
| 172074 | all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; |
| 172075 | sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); |
| 172076 | } |
| 172077 | break; |
| 172078 | case 258: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 172079 | { |
| 172080 | sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); |
| 172081 | yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ |
| 172082 | } |
| 172083 | break; |
| 172084 | case 259: /* trigger_time ::= BEFORE|AFTER */ |
| 172085 | { yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } |
| 172086 | break; |
| 172087 | case 260: /* trigger_time ::= INSTEAD OF */ |
| 172088 | { yymsp[-1].minor.yy394 = TK_INSTEAD;} |
| 172089 | break; |
| 172090 | case 261: /* trigger_time ::= */ |
| 172091 | { yymsp[1].minor.yy394 = TK_BEFORE; } |
| 172092 | break; |
| 172093 | case 262: /* trigger_event ::= DELETE|INSERT */ |
| 172094 | case 263: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==263); |
| 172095 | {yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} |
| 172096 | break; |
| 172097 | case 264: /* trigger_event ::= UPDATE OF idlist */ |
| 172098 | {yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} |
| 172099 | break; |
| 172100 | case 265: /* when_clause ::= */ |
| 172101 | case 284: /* key_opt ::= */ yytestcase(yyruleno==284); |
| 172102 | { yymsp[1].minor.yy528 = 0; } |
| 172103 | break; |
| 172104 | case 266: /* when_clause ::= WHEN expr */ |
| 172105 | case 285: /* key_opt ::= KEY expr */ yytestcase(yyruleno==285); |
| 172106 | { yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } |
| 172107 | break; |
| 172108 | case 267: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 172109 | { |
| 172110 | assert( yymsp[-2].minor.yy33!=0 ); |
| 172111 | yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; |
| 172112 | yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172113 | } |
| 172114 | break; |
| 172115 | case 268: /* trigger_cmd_list ::= trigger_cmd SEMI */ |
| 172116 | { |
| 172117 | assert( yymsp[-1].minor.yy33!=0 ); |
| 172118 | yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172119 | } |
| 172120 | break; |
| 172121 | case 269: /* trnm ::= nm DOT nm */ |
| 172122 | { |
| 172123 | yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; |
| 172124 | sqlite3ErrorMsg(pParse, |
| 172125 | "qualified table names are not allowed on INSERT, UPDATE, and DELETE " |
| 172126 | "statements within triggers"); |
| 172127 | } |
| 172128 | break; |
| 172129 | case 270: /* tridxby ::= INDEXED BY nm */ |
| 172130 | { |
| 172131 | sqlite3ErrorMsg(pParse, |
| 172132 | "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " |
| 172133 | "within triggers"); |
| 172134 | } |
| 172135 | break; |
| 172136 | case 271: /* tridxby ::= NOT INDEXED */ |
| 172137 | { |
| 172138 | sqlite3ErrorMsg(pParse, |
| 172139 | "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " |
| 172140 | "within triggers"); |
| 172141 | } |
| 172142 | break; |
| 172143 | case 272: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 172144 | {yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172145 | yymsp[-8].minor.yy33 = yylhsminor.yy33; |
| 172146 | break; |
| 172147 | case 273: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 172148 | { |
| 172149 | yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ |
| 172150 | } |
| 172151 | yymsp[-7].minor.yy33 = yylhsminor.yy33; |
| 172152 | break; |
| 172153 | case 274: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 172154 | {yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172155 | yymsp[-5].minor.yy33 = yylhsminor.yy33; |
| 172156 | break; |
| 172157 | case 275: /* trigger_cmd ::= scanpt select scanpt */ |
| 172158 | {yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} |
| 172159 | yymsp[-2].minor.yy33 = yylhsminor.yy33; |
| 172160 | break; |
| 172161 | case 276: /* expr ::= RAISE LP IGNORE RP */ |
| 172162 | { |
| 172163 | yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); |
| 172164 | if( yymsp[-3].minor.yy528 ){ |
| 172165 | yymsp[-3].minor.yy528->affExpr = OE_Ignore; |
| 172166 | } |
| 172167 | } |
| 172168 | break; |
| 172169 | case 277: /* expr ::= RAISE LP raisetype COMMA nm RP */ |
| 172170 | { |
| 172171 | yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); |
| 172172 | if( yymsp[-5].minor.yy528 ) { |
| 172173 | yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; |
| 172174 | } |
| 172175 | } |
| 172176 | break; |
| 172177 | case 278: /* raisetype ::= ROLLBACK */ |
| 172178 | {yymsp[0].minor.yy394 = OE_Rollback;} |
| 172179 | break; |
| 172180 | case 280: /* raisetype ::= FAIL */ |
| 172181 | {yymsp[0].minor.yy394 = OE_Fail;} |
| 172182 | break; |
| 172183 | case 281: /* cmd ::= DROP TRIGGER ifexists fullname */ |
| 172184 | { |
| 172185 | sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); |
| 172186 | } |
| 172187 | break; |
| 172188 | case 282: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 172189 | { |
| 172190 | sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); |
| 172191 | } |
| 172192 | break; |
| 172193 | case 283: /* cmd ::= DETACH database_kw_opt expr */ |
| 172194 | { |
| 172195 | sqlite3Detach(pParse, yymsp[0].minor.yy528); |
| 172196 | } |
| 172197 | break; |
| 172198 | case 286: /* cmd ::= REINDEX */ |
| 172199 | {sqlite3Reindex(pParse, 0, 0);} |
| 172200 | break; |
| 172201 | case 287: /* cmd ::= REINDEX nm dbnm */ |
| 172202 | {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172203 | break; |
| 172204 | case 288: /* cmd ::= ANALYZE */ |
| 172205 | {sqlite3Analyze(pParse, 0, 0);} |
| 172206 | break; |
| 172207 | case 289: /* cmd ::= ANALYZE nm dbnm */ |
| 172208 | {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172209 | break; |
| 172210 | case 290: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 172211 | { |
| 172212 | sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); |
| 172213 | } |
| 172214 | break; |
| 172215 | case 291: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 172216 | { |
| 172217 | yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; |
| 172218 | sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); |
| 172219 | } |
| 172220 | break; |
| 172221 | case 292: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 172222 | { |
| 172223 | sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); |
| 172224 | } |
| 172225 | break; |
| 172226 | case 293: /* add_column_fullname ::= fullname */ |
| 172227 | { |
| 172228 | disableLookaside(pParse); |
| 172229 | sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); |
| 172230 | } |
| 172231 | break; |
| 172232 | case 294: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 172233 | { |
| 172234 | sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); |
| 172235 | } |
| 172236 | break; |
| 172237 | case 295: /* cmd ::= create_vtab */ |
| 172238 | {sqlite3VtabFinishParse(pParse,0);} |
| 172239 | break; |
| 172240 | case 296: /* cmd ::= create_vtab LP vtabarglist RP */ |
| 172241 | {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} |
| 172242 | break; |
| 172243 | case 297: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 172244 | { |
| 172245 | sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); |
| 172246 | } |
| 172247 | break; |
| 172248 | case 298: /* vtabarg ::= */ |
| 172249 | {sqlite3VtabArgInit(pParse);} |
| 172250 | break; |
| 172251 | case 299: /* vtabargtoken ::= ANY */ |
| 172252 | case 300: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==300); |
| 172253 | case 301: /* lp ::= LP */ yytestcase(yyruleno==301); |
| 172254 | {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} |
| 172255 | break; |
| 172256 | case 302: /* with ::= WITH wqlist */ |
| 172257 | case 303: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==303); |
| 172258 | { sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } |
| 172259 | break; |
| 172260 | case 304: /* wqas ::= AS */ |
| 172261 | {yymsp[0].minor.yy516 = M10d_Any;} |
| 172262 | break; |
| 172263 | case 305: /* wqas ::= AS MATERIALIZED */ |
| 172264 | {yymsp[-1].minor.yy516 = M10d_Yes;} |
| 172265 | break; |
| 172266 | case 306: /* wqas ::= AS NOT MATERIALIZED */ |
| 172267 | {yymsp[-2].minor.yy516 = M10d_No;} |
| 172268 | break; |
| 172269 | case 307: /* wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 172270 | { |
| 172271 | yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ |
| 172272 | } |
| 172273 | break; |
| 172274 | case 308: /* wqlist ::= wqitem */ |
| 172275 | { |
| 172276 | yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ |
| 172277 | } |
| 172278 | break; |
| 172279 | case 309: /* wqlist ::= wqlist COMMA wqitem */ |
| 172280 | { |
| 172281 | yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); |
| 172282 | } |
| 172283 | break; |
| 172284 | case 310: /* windowdefn_list ::= windowdefn */ |
| 172285 | { yylhsminor.yy41 = yymsp[0].minor.yy41; } |
| 172286 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172287 | break; |
| 172288 | case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 172289 | { |
| 172290 | assert( yymsp[0].minor.yy41!=0 ); |
| 172291 | sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); |
| 172292 | yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; |
| 172293 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172294 | } |
| 172295 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172296 | break; |
| 172297 | case 312: /* windowdefn ::= nm AS LP window RP */ |
| 172298 | { |
| 172299 | if( ALWAYS(yymsp[-1].minor.yy41) ){ |
| 172300 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); |
| 172301 | } |
| 172302 | yylhsminor.yy41 = yymsp[-1].minor.yy41; |
| 172303 | } |
| 172304 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172305 | break; |
| 172306 | case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 172307 | { |
| 172308 | yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); |
| 172309 | } |
| 172310 | break; |
| 172311 | case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 172312 | { |
| 172313 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); |
| 172314 | } |
| 172315 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172316 | break; |
| 172317 | case 315: /* window ::= ORDER BY sortlist frame_opt */ |
| 172318 | { |
| 172319 | yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); |
| 172320 | } |
| 172321 | break; |
| 172322 | case 316: /* window ::= nm ORDER BY sortlist frame_opt */ |
| 172323 | { |
| 172324 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); |
| 172325 | } |
| 172326 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172327 | break; |
| 172328 | case 317: /* window ::= frame_opt */ |
| 172329 | case 336: /* filter_over ::= over_clause */ yytestcase(yyruleno==336); |
| 172330 | { |
| 172331 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172332 | } |
| 172333 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172334 | break; |
| 172335 | case 318: /* window ::= nm frame_opt */ |
| 172336 | { |
| 172337 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); |
| 172338 | } |
| 172339 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172340 | break; |
| 172341 | case 319: /* frame_opt ::= */ |
| 172342 | { |
| 172343 | yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); |
| 172344 | } |
| 172345 | break; |
| 172346 | case 320: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 172347 | { |
| 172348 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); |
| 172349 | } |
| 172350 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172351 | break; |
| 172352 | case 321: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 172353 | { |
| 172354 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); |
| 172355 | } |
| 172356 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172357 | break; |
| 172358 | case 323: /* frame_bound_s ::= frame_bound */ |
| 172359 | case 325: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==325); |
| 172360 | {yylhsminor.yy595 = yymsp[0].minor.yy595;} |
| 172361 | yymsp[0].minor.yy595 = yylhsminor.yy595; |
| 172362 | break; |
| 172363 | case 324: /* frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 172364 | case 326: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==326); |
| 172365 | case 328: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==328); |
| 172366 | {yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} |
| 172367 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172368 | break; |
| 172369 | case 327: /* frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 172370 | {yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} |
| 172371 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172372 | break; |
| 172373 | case 329: /* frame_exclude_opt ::= */ |
| 172374 | {yymsp[1].minor.yy516 = 0;} |
| 172375 | break; |
| 172376 | case 330: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 172377 | {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} |
| 172378 | break; |
| 172379 | case 331: /* frame_exclude ::= NO OTHERS */ |
| 172380 | case 332: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==332); |
| 172381 | {yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} |
| 172382 | break; |
| 172383 | case 333: /* frame_exclude ::= GROUP|TIES */ |
| 172384 | {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} |
| 172385 | break; |
| 172386 | case 334: /* window_clause ::= WINDOW windowdefn_list */ |
| 172387 | { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } |
| 172388 | break; |
| 172389 | case 335: /* filter_over ::= filter_clause over_clause */ |
| 172390 | { |
| 172391 | if( yymsp[0].minor.yy41 ){ |
| 172392 | yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; |
| 172393 | }else{ |
| 172394 | sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); |
| @@ -172395,11 +172718,11 @@ | |
| 172395 | } |
| 172396 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172397 | } |
| 172398 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172399 | break; |
| 172400 | case 337: /* filter_over ::= filter_clause */ |
| 172401 | { |
| 172402 | yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172403 | if( yylhsminor.yy41 ){ |
| 172404 | yylhsminor.yy41->eFrmType = TK_FILTER; |
| 172405 | yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; |
| @@ -172407,68 +172730,69 @@ | |
| 172407 | sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); |
| 172408 | } |
| 172409 | } |
| 172410 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172411 | break; |
| 172412 | case 338: /* over_clause ::= OVER LP window RP */ |
| 172413 | { |
| 172414 | yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; |
| 172415 | assert( yymsp[-3].minor.yy41!=0 ); |
| 172416 | } |
| 172417 | break; |
| 172418 | case 339: /* over_clause ::= OVER nm */ |
| 172419 | { |
| 172420 | yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172421 | if( yymsp[-1].minor.yy41 ){ |
| 172422 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); |
| 172423 | } |
| 172424 | } |
| 172425 | break; |
| 172426 | case 340: /* filter_clause ::= FILTER LP WHERE expr RP */ |
| 172427 | { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } |
| 172428 | break; |
| 172429 | default: |
| 172430 | /* (341) input ::= cmdlist */ yytestcase(yyruleno==341); |
| 172431 | /* (342) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==342); |
| 172432 | /* (343) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=343); |
| 172433 | /* (344) ecmd ::= SEMI */ yytestcase(yyruleno==344); |
| 172434 | /* (345) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==345); |
| 172435 | /* (346) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=346); |
| 172436 | /* (347) trans_opt ::= */ yytestcase(yyruleno==347); |
| 172437 | /* (348) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==348); |
| 172438 | /* (349) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==349); |
| 172439 | /* (350) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==350); |
| 172440 | /* (351) savepoint_opt ::= */ yytestcase(yyruleno==351); |
| 172441 | /* (352) cmd ::= create_table create_table_args */ yytestcase(yyruleno==352); |
| 172442 | /* (353) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=353); |
| 172443 | /* (354) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==354); |
| 172444 | /* (355) columnlist ::= columnname carglist */ yytestcase(yyruleno==355); |
| 172445 | /* (356) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==356); |
| 172446 | /* (357) nm ::= STRING */ yytestcase(yyruleno==357); |
| 172447 | /* (358) typetoken ::= typename */ yytestcase(yyruleno==358); |
| 172448 | /* (359) typename ::= ID|STRING */ yytestcase(yyruleno==359); |
| 172449 | /* (360) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); |
| 172450 | /* (361) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=361); |
| 172451 | /* (362) carglist ::= carglist ccons */ yytestcase(yyruleno==362); |
| 172452 | /* (363) carglist ::= */ yytestcase(yyruleno==363); |
| 172453 | /* (364) ccons ::= NULL onconf */ yytestcase(yyruleno==364); |
| 172454 | /* (365) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==365); |
| 172455 | /* (366) ccons ::= AS generated */ yytestcase(yyruleno==366); |
| 172456 | /* (367) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==367); |
| 172457 | /* (368) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==368); |
| 172458 | /* (369) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=369); |
| 172459 | /* (370) tconscomma ::= */ yytestcase(yyruleno==370); |
| 172460 | /* (371) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=371); |
| 172461 | /* (372) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=372); |
| 172462 | /* (373) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=373); |
| 172463 | /* (374) oneselect ::= values */ yytestcase(yyruleno==374); |
| 172464 | /* (375) sclp ::= selcollist COMMA */ yytestcase(yyruleno==375); |
| 172465 | /* (376) as ::= ID|STRING */ yytestcase(yyruleno==376); |
| 172466 | /* (377) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=377); |
| 172467 | /* (378) returning ::= */ yytestcase(yyruleno==378); |
| 172468 | /* (379) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=379); |
| 172469 | /* (380) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==380); |
| 172470 | /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); |
| 172471 | /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); |
| 172472 | /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); |
| 172473 | /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); |
| 172474 | /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); |
| @@ -178496,11 +178820,11 @@ | |
| 178496 | if( pVfs==0 ) return 0; |
| 178497 | |
| 178498 | /* This function works in milliseconds, but the underlying OsSleep() |
| 178499 | ** API uses microseconds. Hence the 1000's. |
| 178500 | */ |
| 178501 | rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000); |
| 178502 | return rc; |
| 178503 | } |
| 178504 | |
| 178505 | /* |
| 178506 | ** Enable or disable the extended result codes. |
| @@ -199500,10 +199824,11 @@ | |
| 199500 | #define JNODE_REMOVE 0x04 /* Do not output */ |
| 199501 | #define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ |
| 199502 | #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ |
| 199503 | #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ |
| 199504 | #define JNODE_LABEL 0x40 /* Is a label of an object */ |
| 199505 | |
| 199506 | |
| 199507 | /* A single node of parsed JSON |
| 199508 | */ |
| 199509 | struct JsonNode { |
| @@ -199526,25 +199851,27 @@ | |
| 199526 | u32 nNode; /* Number of slots of aNode[] used */ |
| 199527 | u32 nAlloc; /* Number of slots of aNode[] allocated */ |
| 199528 | JsonNode *aNode; /* Array of nodes containing the parse */ |
| 199529 | const char *zJson; /* Original JSON string */ |
| 199530 | u32 *aUp; /* Index of parent of each node */ |
| 199531 | u8 oom; /* Set to true if out of memory */ |
| 199532 | u8 nErr; /* Number of errors seen */ |
| 199533 | u16 iDepth; /* Nesting depth */ |
| 199534 | int nJson; /* Length of the zJson string in bytes */ |
| 199535 | u32 iHold; /* Replace cache line with the lowest iHold value */ |
| 199536 | }; |
| 199537 | |
| 199538 | /* |
| 199539 | ** Maximum nesting depth of JSON for this implementation. |
| 199540 | ** |
| 199541 | ** This limit is needed to avoid a stack overflow in the recursive |
| 199542 | ** descent parser. A depth of 2000 is far deeper than any sane JSON |
| 199543 | ** should go. |
| 199544 | */ |
| 199545 | #define JSON_MAX_DEPTH 2000 |
| 199546 | |
| 199547 | /************************************************************************** |
| 199548 | ** Utility routines for dealing with JsonString objects |
| 199549 | **************************************************************************/ |
| 199550 | |
| @@ -199689,10 +200016,133 @@ | |
| 199689 | p->zBuf[p->nUsed++] = c; |
| 199690 | } |
| 199691 | p->zBuf[p->nUsed++] = '"'; |
| 199692 | assert( p->nUsed<p->nAlloc ); |
| 199693 | } |
| 199694 | |
| 199695 | /* |
| 199696 | ** Append a function parameter value to the JSON string under |
| 199697 | ** construction. |
| 199698 | */ |
| @@ -199820,21 +200270,42 @@ | |
| 199820 | case JSON_FALSE: { |
| 199821 | jsonAppendRaw(pOut, "false", 5); |
| 199822 | break; |
| 199823 | } |
| 199824 | case JSON_STRING: { |
| 199825 | if( pNode->jnFlags & JNODE_RAW ){ |
| 199826 | assert( pNode->eU==1 ); |
| 199827 | jsonAppendString(pOut, pNode->u.zJContent, pNode->n); |
| 199828 | break; |
| 199829 | } |
| 199830 | /* no break */ deliberate_fall_through |
| 199831 | } |
| 199832 | case JSON_REAL: |
| 199833 | case JSON_INT: { |
| 199834 | assert( pNode->eU==1 ); |
| 199835 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 199836 | break; |
| 199837 | } |
| 199838 | case JSON_ARRAY: { |
| 199839 | u32 j = 1; |
| 199840 | jsonAppendChar(pOut, '['); |
| @@ -199946,63 +200417,45 @@ | |
| 199946 | sqlite3_result_int(pCtx, 0); |
| 199947 | break; |
| 199948 | } |
| 199949 | case JSON_INT: { |
| 199950 | sqlite3_int64 i = 0; |
| 199951 | const char *z; |
| 199952 | assert( pNode->eU==1 ); |
| 199953 | z = pNode->u.zJContent; |
| 199954 | if( z[0]=='-' ){ z++; } |
| 199955 | while( z[0]>='0' && z[0]<='9' ){ |
| 199956 | unsigned v = *(z++) - '0'; |
| 199957 | if( i>=LARGEST_INT64/10 ){ |
| 199958 | if( i>LARGEST_INT64/10 ) goto int_as_real; |
| 199959 | if( z[0]>='0' && z[0]<='9' ) goto int_as_real; |
| 199960 | if( v==9 ) goto int_as_real; |
| 199961 | if( v==8 ){ |
| 199962 | if( pNode->u.zJContent[0]=='-' ){ |
| 199963 | sqlite3_result_int64(pCtx, SMALLEST_INT64); |
| 199964 | goto int_done; |
| 199965 | }else{ |
| 199966 | goto int_as_real; |
| 199967 | } |
| 199968 | } |
| 199969 | } |
| 199970 | i = i*10 + v; |
| 199971 | } |
| 199972 | if( pNode->u.zJContent[0]=='-' ){ i = -i; } |
| 199973 | sqlite3_result_int64(pCtx, i); |
| 199974 | int_done: |
| 199975 | break; |
| 199976 | int_as_real: ; /* no break */ deliberate_fall_through |
| 199977 | } |
| 199978 | case JSON_REAL: { |
| 199979 | double r; |
| 199980 | #ifdef SQLITE_AMALGAMATION |
| 199981 | const char *z; |
| 199982 | assert( pNode->eU==1 ); |
| 199983 | z = pNode->u.zJContent; |
| 199984 | sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); |
| 199985 | #else |
| 199986 | assert( pNode->eU==1 ); |
| 199987 | r = strtod(pNode->u.zJContent, 0); |
| 199988 | #endif |
| 199989 | sqlite3_result_double(pCtx, r); |
| 199990 | break; |
| 199991 | } |
| 199992 | case JSON_STRING: { |
| 199993 | #if 0 /* Never happens because JNODE_RAW is only set by json_set(), |
| 199994 | ** json_insert() and json_replace() and those routines do not |
| 199995 | ** call jsonReturn() */ |
| 199996 | if( pNode->jnFlags & JNODE_RAW ){ |
| 199997 | assert( pNode->eU==1 ); |
| 199998 | sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, |
| 199999 | SQLITE_TRANSIENT); |
| 200000 | }else |
| 200001 | #endif |
| 200002 | assert( (pNode->jnFlags & JNODE_RAW)==0 ); |
| 200003 | if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ |
| 200004 | /* JSON formatted without any backslash-escapes */ |
| 200005 | assert( pNode->eU==1 ); |
| 200006 | sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, |
| 200007 | SQLITE_TRANSIENT); |
| 200008 | }else{ |
| @@ -200010,22 +200463,21 @@ | |
| 200010 | u32 i; |
| 200011 | u32 n = pNode->n; |
| 200012 | const char *z; |
| 200013 | char *zOut; |
| 200014 | u32 j; |
| 200015 | assert( pNode->eU==1 ); |
| 200016 | z = pNode->u.zJContent; |
| 200017 | zOut = sqlite3_malloc( n+1 ); |
| 200018 | if( zOut==0 ){ |
| 200019 | sqlite3_result_error_nomem(pCtx); |
| 200020 | break; |
| 200021 | } |
| 200022 | for(i=1, j=0; i<n-1; i++){ |
| 200023 | char c = z[i]; |
| 200024 | if( c!='\\' ){ |
| 200025 | zOut[j++] = c; |
| 200026 | }else{ |
| 200027 | c = z[++i]; |
| 200028 | if( c=='u' ){ |
| 200029 | u32 v = jsonHexToInt4(z+i+1); |
| 200030 | i += 4; |
| 200031 | if( v==0 ) break; |
| @@ -200053,26 +200505,44 @@ | |
| 200053 | zOut[j++] = 0xe0 | (v>>12); |
| 200054 | zOut[j++] = 0x80 | ((v>>6)&0x3f); |
| 200055 | zOut[j++] = 0x80 | (v&0x3f); |
| 200056 | } |
| 200057 | } |
| 200058 | }else{ |
| 200059 | if( c=='b' ){ |
| 200060 | c = '\b'; |
| 200061 | }else if( c=='f' ){ |
| 200062 | c = '\f'; |
| 200063 | }else if( c=='n' ){ |
| 200064 | c = '\n'; |
| 200065 | }else if( c=='r' ){ |
| 200066 | c = '\r'; |
| 200067 | }else if( c=='t' ){ |
| 200068 | c = '\t'; |
| 200069 | } |
| 200070 | zOut[j++] = c; |
| 200071 | } |
| 200072 | } |
| 200073 | } |
| 200074 | zOut[j] = 0; |
| 200075 | sqlite3_result_text(pCtx, zOut, j, sqlite3_free); |
| 200076 | } |
| 200077 | break; |
| 200078 | } |
| @@ -200136,28 +200606,159 @@ | |
| 200136 | JsonNode *p; |
| 200137 | if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ |
| 200138 | return jsonParseAddNodeExpand(pParse, eType, n, zContent); |
| 200139 | } |
| 200140 | p = &pParse->aNode[pParse->nNode]; |
| 200141 | p->eType = (u8)eType; |
| 200142 | p->jnFlags = 0; |
| 200143 | VVA( p->eU = zContent ? 1 : 0 ); |
| 200144 | p->n = n; |
| 200145 | p->u.zJContent = zContent; |
| 200146 | return pParse->nNode++; |
| 200147 | } |
| 200148 | |
| 200149 | /* |
| 200150 | ** Return true if z[] begins with 4 (or more) hexadecimal digits |
| 200151 | */ |
| 200152 | static int jsonIs4Hex(const char *z){ |
| 200153 | int i; |
| 200154 | for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0; |
| 200155 | return 1; |
| 200156 | } |
| 200157 | |
| 200158 | #ifdef SQLITE_ENABLE_JSON_NAN_INF |
| 200159 | /* |
| 200160 | ** Extra floating-point literals to allow in JSON. |
| 200161 | */ |
| 200162 | static const struct NanInfName { |
| 200163 | char c1; |
| @@ -200172,204 +200773,455 @@ | |
| 200172 | { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, |
| 200173 | { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, |
| 200174 | { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, |
| 200175 | { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, |
| 200176 | }; |
| 200177 | #endif /* SQLITE_ENABLE_JSON_NAN_INF */ |
| 200178 | |
| 200179 | /* |
| 200180 | ** Parse a single JSON value which begins at pParse->zJson[i]. Return the |
| 200181 | ** index of the first character past the end of the value parsed. |
| 200182 | ** |
| 200183 | ** Return negative for a syntax error. Special cases: return -2 if the |
| 200184 | ** first non-whitespace character is '}' and return -3 if the first |
| 200185 | ** non-whitespace character is ']'. |
| 200186 | */ |
| 200187 | static int jsonParseValue(JsonParse *pParse, u32 i){ |
| 200188 | char c; |
| 200189 | u32 j; |
| 200190 | int iThis; |
| 200191 | int x; |
| 200192 | JsonNode *pNode; |
| 200193 | const char *z = pParse->zJson; |
| 200194 | while( fast_isspace(z[i]) ){ i++; } |
| 200195 | if( (c = z[i])=='{' ){ |
| 200196 | /* Parse object */ |
| 200197 | iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); |
| 200198 | if( iThis<0 ) return -1; |
| 200199 | for(j=i+1;;j++){ |
| 200200 | while( fast_isspace(z[j]) ){ j++; } |
| 200201 | if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; |
| 200202 | x = jsonParseValue(pParse, j); |
| 200203 | if( x<0 ){ |
| 200204 | pParse->iDepth--; |
| 200205 | if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1; |
| 200206 | return -1; |
| 200207 | } |
| 200208 | if( pParse->oom ) return -1; |
| 200209 | pNode = &pParse->aNode[pParse->nNode-1]; |
| 200210 | if( pNode->eType!=JSON_STRING ) return -1; |
| 200211 | pNode->jnFlags |= JNODE_LABEL; |
| 200212 | j = x; |
| 200213 | while( fast_isspace(z[j]) ){ j++; } |
| 200214 | if( z[j]!=':' ) return -1; |
| 200215 | j++; |
| 200216 | x = jsonParseValue(pParse, j); |
| 200217 | pParse->iDepth--; |
| 200218 | if( x<0 ) return -1; |
| 200219 | j = x; |
| 200220 | while( fast_isspace(z[j]) ){ j++; } |
| 200221 | c = z[j]; |
| 200222 | if( c==',' ) continue; |
| 200223 | if( c!='}' ) return -1; |
| 200224 | break; |
| 200225 | } |
| 200226 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200227 | return j+1; |
| 200228 | }else if( c=='[' ){ |
| 200229 | /* Parse array */ |
| 200230 | iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); |
| 200231 | if( iThis<0 ) return -1; |
| 200232 | memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); |
| 200233 | for(j=i+1;;j++){ |
| 200234 | while( fast_isspace(z[j]) ){ j++; } |
| 200235 | if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1; |
| 200236 | x = jsonParseValue(pParse, j); |
| 200237 | pParse->iDepth--; |
| 200238 | if( x<0 ){ |
| 200239 | if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1; |
| 200240 | return -1; |
| 200241 | } |
| 200242 | j = x; |
| 200243 | while( fast_isspace(z[j]) ){ j++; } |
| 200244 | c = z[j]; |
| 200245 | if( c==',' ) continue; |
| 200246 | if( c!=']' ) return -1; |
| 200247 | break; |
| 200248 | } |
| 200249 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200250 | return j+1; |
| 200251 | }else if( c=='"' ){ |
| 200252 | /* Parse string */ |
| 200253 | u8 jnFlags = 0; |
| 200254 | j = i+1; |
| 200255 | for(;;){ |
| 200256 | c = z[j]; |
| 200257 | if( (c & ~0x1f)==0 ){ |
| 200258 | /* Control characters are not allowed in strings */ |
| 200259 | return -1; |
| 200260 | } |
| 200261 | if( c=='\\' ){ |
| 200262 | c = z[++j]; |
| 200263 | if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' |
| 200264 | || c=='n' || c=='r' || c=='t' |
| 200265 | || (c=='u' && jsonIs4Hex(z+j+1)) ){ |
| 200266 | jnFlags = JNODE_ESCAPE; |
| 200267 | }else{ |
| 200268 | return -1; |
| 200269 | } |
| 200270 | }else if( c=='"' ){ |
| 200271 | break; |
| 200272 | } |
| 200273 | j++; |
| 200274 | } |
| 200275 | jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]); |
| 200276 | if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags; |
| 200277 | return j+1; |
| 200278 | }else if( c=='n' |
| 200279 | && strncmp(z+i,"null",4)==0 |
| 200280 | && !sqlite3Isalnum(z[i+4]) ){ |
| 200281 | jsonParseAddNode(pParse, JSON_NULL, 0, 0); |
| 200282 | return i+4; |
| 200283 | }else if( c=='t' |
| 200284 | && strncmp(z+i,"true",4)==0 |
| 200285 | && !sqlite3Isalnum(z[i+4]) ){ |
| 200286 | jsonParseAddNode(pParse, JSON_TRUE, 0, 0); |
| 200287 | return i+4; |
| 200288 | }else if( c=='f' |
| 200289 | && strncmp(z+i,"false",5)==0 |
| 200290 | && !sqlite3Isalnum(z[i+5]) ){ |
| 200291 | jsonParseAddNode(pParse, JSON_FALSE, 0, 0); |
| 200292 | return i+5; |
| 200293 | }else if( c=='-' || (c>='0' && c<='9') ){ |
| 200294 | /* Parse number */ |
| 200295 | u8 seenDP = 0; |
| 200296 | u8 seenE = 0; |
| 200297 | assert( '-' < '0' ); |
| 200298 | if( c<='0' ){ |
| 200299 | j = c=='-' ? i+1 : i; |
| 200300 | if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1; |
| 200301 | } |
| 200302 | j = i+1; |
| 200303 | for(;; j++){ |
| 200304 | c = z[j]; |
| 200305 | if( c>='0' && c<='9' ) continue; |
| 200306 | if( c=='.' ){ |
| 200307 | if( z[j-1]=='-' ) return -1; |
| 200308 | if( seenDP ) return -1; |
| 200309 | seenDP = 1; |
| 200310 | continue; |
| 200311 | } |
| 200312 | if( c=='e' || c=='E' ){ |
| 200313 | if( z[j-1]<'0' ) return -1; |
| 200314 | if( seenE ) return -1; |
| 200315 | seenDP = seenE = 1; |
| 200316 | c = z[j+1]; |
| 200317 | if( c=='+' || c=='-' ){ |
| 200318 | j++; |
| 200319 | c = z[j+1]; |
| 200320 | } |
| 200321 | if( c<'0' || c>'9' ) return -1; |
| 200322 | continue; |
| 200323 | } |
| 200324 | #ifdef SQLITE_ENABLE_JSON_NAN_INF |
| 200325 | /* Non-standard JSON: Allow "-Inf" (in any case) |
| 200326 | ** to be understood as floating point literals. */ |
| 200327 | if( (c=='i' || c=='I') |
| 200328 | && j==i+1 |
| 200329 | && z[i]=='-' |
| 200330 | && sqlite3StrNICmp(&z[j], "inf",3)==0 |
| 200331 | ){ |
| 200332 | if( !sqlite3Isalnum(z[j+3]) ){ |
| 200333 | jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); |
| 200334 | return i+4; |
| 200335 | }else if( (sqlite3StrNICmp(&z[j],"infinity",8)==0 && |
| 200336 | !sqlite3Isalnum(z[j+8])) ){ |
| 200337 | jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); |
| 200338 | return i+9; |
| 200339 | } |
| 200340 | } |
| 200341 | #endif |
| 200342 | break; |
| 200343 | } |
| 200344 | if( z[j-1]<'0' ) return -1; |
| 200345 | jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT, |
| 200346 | j - i, &z[i]); |
| 200347 | return j; |
| 200348 | }else if( c=='}' ){ |
| 200349 | return -2; /* End of {...} */ |
| 200350 | }else if( c==']' ){ |
| 200351 | return -3; /* End of [...] */ |
| 200352 | }else if( c==0 ){ |
| 200353 | return 0; /* End of file */ |
| 200354 | }else{ |
| 200355 | #ifdef SQLITE_ENABLE_JSON_NAN_INF |
| 200356 | int k, nn; |
| 200357 | for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){ |
| 200358 | if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue; |
| 200359 | nn = aNanInfName[k].n; |
| 200360 | if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){ |
| 200361 | continue; |
| 200362 | } |
| 200363 | if( sqlite3Isalnum(z[i+nn]) ) continue; |
| 200364 | jsonParseAddNode(pParse, aNanInfName[k].eType, |
| 200365 | aNanInfName[k].nRepl, aNanInfName[k].zRepl); |
| 200366 | return i + nn; |
| 200367 | } |
| 200368 | #endif |
| 200369 | return -1; /* Syntax error */ |
| 200370 | } |
| 200371 | } |
| 200372 | |
| 200373 | /* |
| 200374 | ** Parse a complete JSON string. Return 0 on success or non-zero if there |
| 200375 | ** are any errors. If an error occurs, free all memory associated with |
| @@ -200389,11 +201241,18 @@ | |
| 200389 | i = jsonParseValue(pParse, 0); |
| 200390 | if( pParse->oom ) i = -1; |
| 200391 | if( i>0 ){ |
| 200392 | assert( pParse->iDepth==0 ); |
| 200393 | while( fast_isspace(zJson[i]) ) i++; |
| 200394 | if( zJson[i] ) i = -1; |
| 200395 | } |
| 200396 | if( i<=0 ){ |
| 200397 | if( pCtx!=0 ){ |
| 200398 | if( pParse->oom ){ |
| 200399 | sqlite3_result_error_nomem(pCtx); |
| @@ -200460,10 +201319,19 @@ | |
| 200460 | ** of the argv array. Use the sqlite3_get_auxdata() cache for this |
| 200461 | ** parse if it is available. If the cache is not available or if it |
| 200462 | ** is no longer valid, parse the JSON again and return the new parse, |
| 200463 | ** and also register the new parse so that it will be available for |
| 200464 | ** future sqlite3_get_auxdata() calls. |
| 200465 | */ |
| 200466 | static JsonParse *jsonParseCached( |
| 200467 | sqlite3_context *pCtx, |
| 200468 | sqlite3_value **argv, |
| 200469 | sqlite3_context *pErrCtx |
| @@ -200509,10 +201377,14 @@ | |
| 200509 | } |
| 200510 | memset(p, 0, sizeof(*p)); |
| 200511 | p->zJson = (char*)&p[1]; |
| 200512 | memcpy((char*)p->zJson, zJson, nJson+1); |
| 200513 | if( jsonParse(p, pErrCtx, p->zJson) ){ |
| 200514 | sqlite3_free(p); |
| 200515 | return 0; |
| 200516 | } |
| 200517 | p->nJson = nJson; |
| 200518 | p->iHold = iMaxHold+1; |
| @@ -200523,19 +201395,28 @@ | |
| 200523 | |
| 200524 | /* |
| 200525 | ** Compare the OBJECT label at pNode against zKey,nKey. Return true on |
| 200526 | ** a match. |
| 200527 | */ |
| 200528 | static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){ |
| 200529 | assert( pNode->eU==1 ); |
| 200530 | if( pNode->jnFlags & JNODE_RAW ){ |
| 200531 | if( pNode->n!=nKey ) return 0; |
| 200532 | return strncmp(pNode->u.zJContent, zKey, nKey)==0; |
| 200533 | }else{ |
| 200534 | if( pNode->n!=nKey+2 ) return 0; |
| 200535 | return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; |
| 200536 | } |
| 200537 | } |
| 200538 | |
| 200539 | /* forward declaration */ |
| 200540 | static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); |
| 200541 | |
| @@ -201003,11 +201884,11 @@ | |
| 201003 | if( argc==2 ){ |
| 201004 | /* With a single PATH argument */ |
| 201005 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 201006 | if( zPath==0 ) return; |
| 201007 | if( flags & JSON_ABPATH ){ |
| 201008 | if( zPath[0]!='$' ){ |
| 201009 | /* The -> and ->> operators accept abbreviated PATH arguments. This |
| 201010 | ** is mostly for compatibility with PostgreSQL, but also for |
| 201011 | ** convenience. |
| 201012 | ** |
| 201013 | ** NUMBER ==> $[NUMBER] // PG compatible |
| @@ -201094,16 +201975,14 @@ | |
| 201094 | assert( pPatch[i].eType==JSON_STRING ); |
| 201095 | assert( pPatch[i].jnFlags & JNODE_LABEL ); |
| 201096 | assert( pPatch[i].eU==1 ); |
| 201097 | nKey = pPatch[i].n; |
| 201098 | zKey = pPatch[i].u.zJContent; |
| 201099 | assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); |
| 201100 | for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){ |
| 201101 | assert( pTarget[j].eType==JSON_STRING ); |
| 201102 | assert( pTarget[j].jnFlags & JNODE_LABEL ); |
| 201103 | assert( (pPatch[i].jnFlags & JNODE_RAW)==0 ); |
| 201104 | if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){ |
| 201105 | if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; |
| 201106 | if( pPatch[i+1].eType==JSON_NULL ){ |
| 201107 | pTarget[j+1].jnFlags |= JNODE_REMOVE; |
| 201108 | }else{ |
| 201109 | JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); |
| @@ -201386,22 +202265,83 @@ | |
| 201386 | } |
| 201387 | |
| 201388 | /* |
| 201389 | ** json_valid(JSON) |
| 201390 | ** |
| 201391 | ** Return 1 if JSON is a well-formed JSON string according to RFC-7159. |
| 201392 | ** Return 0 otherwise. |
| 201393 | */ |
| 201394 | static void jsonValidFunc( |
| 201395 | sqlite3_context *ctx, |
| 201396 | int argc, |
| 201397 | sqlite3_value **argv |
| 201398 | ){ |
| 201399 | JsonParse *p; /* The parse */ |
| 201400 | UNUSED_PARAMETER(argc); |
| 201401 | p = jsonParseCached(ctx, argv, 0); |
| 201402 | sqlite3_result_int(ctx, p!=0); |
| 201403 | } |
| 201404 | |
| 201405 | |
| 201406 | /**************************************************************************** |
| 201407 | ** Aggregate SQL function implementations |
| @@ -201741,18 +202681,20 @@ | |
| 201741 | assert( pNode->eType==JSON_STRING ); |
| 201742 | assert( pNode->jnFlags & JNODE_LABEL ); |
| 201743 | assert( pNode->eU==1 ); |
| 201744 | z = pNode->u.zJContent; |
| 201745 | nn = pNode->n; |
| 201746 | assert( nn>=2 ); |
| 201747 | assert( z[0]=='"' ); |
| 201748 | assert( z[nn-1]=='"' ); |
| 201749 | if( nn>2 && sqlite3Isalpha(z[1]) ){ |
| 201750 | for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){} |
| 201751 | if( jj==nn-1 ){ |
| 201752 | z++; |
| 201753 | nn -= 2; |
| 201754 | } |
| 201755 | } |
| 201756 | jsonPrintf(nn+2, pStr, ".%.*s", nn, z); |
| 201757 | } |
| 201758 | |
| @@ -202108,10 +203050,11 @@ | |
| 202108 | static FuncDef aJsonFunc[] = { |
| 202109 | JFUNCTION(json, 1, 0, jsonRemoveFunc), |
| 202110 | JFUNCTION(json_array, -1, 0, jsonArrayFunc), |
| 202111 | JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), |
| 202112 | JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), |
| 202113 | JFUNCTION(json_extract, -1, 0, jsonExtractFunc), |
| 202114 | JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), |
| 202115 | JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), |
| 202116 | JFUNCTION(json_insert, -1, 0, jsonSetFunc), |
| 202117 | JFUNCTION(json_object, -1, 0, jsonObjectFunc), |
| @@ -202632,20 +203575,21 @@ | |
| 202632 | ** using C-preprocessor macros. If that is unsuccessful, or if |
| 202633 | ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined |
| 202634 | ** at run-time. |
| 202635 | */ |
| 202636 | #ifndef SQLITE_BYTEORDER |
| 202637 | #if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ |
| 202638 | defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ |
| 202639 | defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ |
| 202640 | defined(__arm__) |
| 202641 | # define SQLITE_BYTEORDER 1234 |
| 202642 | #elif defined(sparc) || defined(__ppc__) |
| 202643 | # define SQLITE_BYTEORDER 4321 |
| 202644 | #else |
| 202645 | # define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ |
| 202646 | #endif |
| 202647 | #endif |
| 202648 | |
| 202649 | |
| 202650 | /* What version of MSVC is being used. 0 means MSVC is not being used */ |
| 202651 | #ifndef MSVC_VERSION |
| @@ -213201,15 +214145,22 @@ | |
| 213201 | static int rbuLockDatabase(sqlite3 *db){ |
| 213202 | int rc = SQLITE_OK; |
| 213203 | sqlite3_file *fd = 0; |
| 213204 | |
| 213205 | sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); |
| 213206 | if( fd==0 ){ |
| 213207 | sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); |
| 213208 | } |
| 213209 | |
| 213210 | if( fd->pMethods ){ |
| 213211 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); |
| 213212 | if( rc==SQLITE_OK ){ |
| 213213 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); |
| 213214 | } |
| 213215 | } |
| @@ -216825,10 +217776,12 @@ | |
| 216825 | # define SESSIONS_STRM_CHUNK_SIZE 64 |
| 216826 | # else |
| 216827 | # define SESSIONS_STRM_CHUNK_SIZE 1024 |
| 216828 | # endif |
| 216829 | #endif |
| 216830 | |
| 216831 | static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; |
| 216832 | |
| 216833 | typedef struct SessionHook SessionHook; |
| 216834 | struct SessionHook { |
| @@ -216847,10 +217800,11 @@ | |
| 216847 | char *zDb; /* Name of database session is attached to */ |
| 216848 | int bEnableSize; /* True if changeset_size() enabled */ |
| 216849 | int bEnable; /* True if currently recording */ |
| 216850 | int bIndirect; /* True if all changes are indirect */ |
| 216851 | int bAutoAttach; /* True to auto-attach tables */ |
| 216852 | int rc; /* Non-zero if an error has occurred */ |
| 216853 | void *pFilterCtx; /* First argument to pass to xTableFilter */ |
| 216854 | int (*xTableFilter)(void *pCtx, const char *zTab); |
| 216855 | i64 nMalloc; /* Number of bytes of data allocated */ |
| 216856 | i64 nMaxChangesetSize; |
| @@ -216923,10 +217877,11 @@ | |
| 216923 | struct SessionTable { |
| 216924 | SessionTable *pNext; |
| 216925 | char *zName; /* Local name of table */ |
| 216926 | int nCol; /* Number of columns in table zName */ |
| 216927 | int bStat1; /* True if this is sqlite_stat1 */ |
| 216928 | const char **azCol; /* Column names */ |
| 216929 | u8 *abPK; /* Array of primary key flags */ |
| 216930 | int nEntry; /* Total number of entries in hash table */ |
| 216931 | int nChange; /* Size of apChange[] array */ |
| 216932 | SessionChange **apChange; /* Hash table buckets */ |
| @@ -217315,60 +218270,66 @@ | |
| 217315 | ** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned |
| 217316 | ** and the output variables are set as described above. |
| 217317 | */ |
| 217318 | static int sessionPreupdateHash( |
| 217319 | sqlite3_session *pSession, /* Session object that owns pTab */ |
| 217320 | SessionTable *pTab, /* Session table handle */ |
| 217321 | int bNew, /* True to hash the new.* PK */ |
| 217322 | int *piHash, /* OUT: Hash value */ |
| 217323 | int *pbNullPK /* OUT: True if there are NULL values in PK */ |
| 217324 | ){ |
| 217325 | unsigned int h = 0; /* Hash value to return */ |
| 217326 | int i; /* Used to iterate through columns */ |
| 217327 | |
| 217328 | assert( *pbNullPK==0 ); |
| 217329 | assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); |
| 217330 | for(i=0; i<pTab->nCol; i++){ |
| 217331 | if( pTab->abPK[i] ){ |
| 217332 | int rc; |
| 217333 | int eType; |
| 217334 | sqlite3_value *pVal; |
| 217335 | |
| 217336 | if( bNew ){ |
| 217337 | rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); |
| 217338 | }else{ |
| 217339 | rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); |
| 217340 | } |
| 217341 | if( rc!=SQLITE_OK ) return rc; |
| 217342 | |
| 217343 | eType = sqlite3_value_type(pVal); |
| 217344 | h = sessionHashAppendType(h, eType); |
| 217345 | if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ |
| 217346 | i64 iVal; |
| 217347 | if( eType==SQLITE_INTEGER ){ |
| 217348 | iVal = sqlite3_value_int64(pVal); |
| 217349 | }else{ |
| 217350 | double rVal = sqlite3_value_double(pVal); |
| 217351 | assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); |
| 217352 | memcpy(&iVal, &rVal, 8); |
| 217353 | } |
| 217354 | h = sessionHashAppendI64(h, iVal); |
| 217355 | }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 217356 | const u8 *z; |
| 217357 | int n; |
| 217358 | if( eType==SQLITE_TEXT ){ |
| 217359 | z = (const u8 *)sqlite3_value_text(pVal); |
| 217360 | }else{ |
| 217361 | z = (const u8 *)sqlite3_value_blob(pVal); |
| 217362 | } |
| 217363 | n = sqlite3_value_bytes(pVal); |
| 217364 | if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; |
| 217365 | h = sessionHashAppendBlob(h, n, z); |
| 217366 | }else{ |
| 217367 | assert( eType==SQLITE_NULL ); |
| 217368 | assert( pTab->bStat1==0 || i!=1 ); |
| 217369 | *pbNullPK = 1; |
| 217370 | } |
| 217371 | } |
| 217372 | } |
| 217373 | |
| 217374 | *piHash = (h % pTab->nChange); |
| @@ -217647,16 +218608,22 @@ | |
| 217647 | ** if the pre-update-hook does not affect the same row as pChange, it returns |
| 217648 | ** false. |
| 217649 | */ |
| 217650 | static int sessionPreupdateEqual( |
| 217651 | sqlite3_session *pSession, /* Session object that owns SessionTable */ |
| 217652 | SessionTable *pTab, /* Table associated with change */ |
| 217653 | SessionChange *pChange, /* Change to compare to */ |
| 217654 | int op /* Current pre-update operation */ |
| 217655 | ){ |
| 217656 | int iCol; /* Used to iterate through columns */ |
| 217657 | u8 *a = pChange->aRecord; /* Cursor used to scan change record */ |
| 217658 | |
| 217659 | assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); |
| 217660 | for(iCol=0; iCol<pTab->nCol; iCol++){ |
| 217661 | if( !pTab->abPK[iCol] ){ |
| 217662 | a += sessionSerialLen(a); |
| @@ -217798,11 +218765,12 @@ | |
| 217798 | const char *zDb, /* Name of attached database (e.g. "main") */ |
| 217799 | const char *zThis, /* Table name */ |
| 217800 | int *pnCol, /* OUT: number of columns */ |
| 217801 | const char **pzTab, /* OUT: Copy of zThis */ |
| 217802 | const char ***pazCol, /* OUT: Array of column names for table */ |
| 217803 | u8 **pabPK /* OUT: Array of booleans - true for PK col */ |
| 217804 | ){ |
| 217805 | char *zPragma; |
| 217806 | sqlite3_stmt *pStmt; |
| 217807 | int rc; |
| 217808 | sqlite3_int64 nByte; |
| @@ -217810,10 +218778,11 @@ | |
| 217810 | int nThis; |
| 217811 | int i; |
| 217812 | u8 *pAlloc = 0; |
| 217813 | char **azCol = 0; |
| 217814 | u8 *abPK = 0; |
| 217815 | |
| 217816 | assert( pazCol && pabPK ); |
| 217817 | |
| 217818 | nThis = sqlite3Strlen30(zThis); |
| 217819 | if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ |
| @@ -217854,14 +218823,19 @@ | |
| 217854 | if( pzTab ) *pzTab = 0; |
| 217855 | return rc; |
| 217856 | } |
| 217857 | |
| 217858 | nByte = nThis + 1; |
| 217859 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 217860 | nByte += sqlite3_column_bytes(pStmt, 1); |
| 217861 | nDbCol++; |
| 217862 | } |
| 217863 | rc = sqlite3_reset(pStmt); |
| 217864 | |
| 217865 | if( rc==SQLITE_OK ){ |
| 217866 | nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); |
| 217867 | pAlloc = sessionMalloc64(pSession, nByte); |
| @@ -217879,10 +218853,18 @@ | |
| 217879 | *pzTab = (char *)pAlloc; |
| 217880 | pAlloc += nThis+1; |
| 217881 | } |
| 217882 | |
| 217883 | i = 0; |
| 217884 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 217885 | int nName = sqlite3_column_bytes(pStmt, 1); |
| 217886 | const unsigned char *zName = sqlite3_column_text(pStmt, 1); |
| 217887 | if( zName==0 ) break; |
| 217888 | memcpy(pAlloc, zName, nName+1); |
| @@ -217890,11 +218872,10 @@ | |
| 217890 | pAlloc += nName+1; |
| 217891 | abPK[i] = sqlite3_column_int(pStmt, 5); |
| 217892 | i++; |
| 217893 | } |
| 217894 | rc = sqlite3_reset(pStmt); |
| 217895 | |
| 217896 | } |
| 217897 | |
| 217898 | /* If successful, populate the output variables. Otherwise, zero them and |
| 217899 | ** free any allocation made. An error code will be returned in this case. |
| 217900 | */ |
| @@ -217907,10 +218888,11 @@ | |
| 217907 | *pabPK = 0; |
| 217908 | *pnCol = 0; |
| 217909 | if( pzTab ) *pzTab = 0; |
| 217910 | sessionFree(pSession, azCol); |
| 217911 | } |
| 217912 | sqlite3_finalize(pStmt); |
| 217913 | return rc; |
| 217914 | } |
| 217915 | |
| 217916 | /* |
| @@ -217928,11 +218910,12 @@ | |
| 217928 | static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ |
| 217929 | if( pTab->nCol==0 ){ |
| 217930 | u8 *abPK; |
| 217931 | assert( pTab->azCol==0 || pTab->abPK==0 ); |
| 217932 | pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, |
| 217933 | pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK |
| 217934 | ); |
| 217935 | if( pSession->rc==SQLITE_OK ){ |
| 217936 | int i; |
| 217937 | for(i=0; i<pTab->nCol; i++){ |
| 217938 | if( abPK[i] ){ |
| @@ -218000,10 +218983,11 @@ | |
| 218000 | SessionTable *pTab, /* Table that change applies to */ |
| 218001 | SessionChange *pC /* Update pC->nMaxSize */ |
| 218002 | ){ |
| 218003 | i64 nNew = 2; |
| 218004 | if( pC->op==SQLITE_INSERT ){ |
| 218005 | if( op!=SQLITE_DELETE ){ |
| 218006 | int ii; |
| 218007 | for(ii=0; ii<pTab->nCol; ii++){ |
| 218008 | sqlite3_value *p = 0; |
| 218009 | pSession->hook.xNew(pSession->hook.pCtx, ii, &p); |
| @@ -218016,16 +219000,20 @@ | |
| 218016 | nNew += pC->nRecord; |
| 218017 | } |
| 218018 | }else{ |
| 218019 | int ii; |
| 218020 | u8 *pCsr = pC->aRecord; |
| 218021 | for(ii=0; ii<pTab->nCol; ii++){ |
| 218022 | int bChanged = 1; |
| 218023 | int nOld = 0; |
| 218024 | int eType; |
| 218025 | sqlite3_value *p = 0; |
| 218026 | pSession->hook.xNew(pSession->hook.pCtx, ii, &p); |
| 218027 | if( p==0 ){ |
| 218028 | return SQLITE_NOMEM; |
| 218029 | } |
| 218030 | |
| 218031 | eType = *pCsr++; |
| @@ -218100,10 +219088,11 @@ | |
| 218100 | ** Unless one is already present or an error occurs, an entry is added |
| 218101 | ** to the changed-rows hash table associated with table pTab. |
| 218102 | */ |
| 218103 | static void sessionPreupdateOneChange( |
| 218104 | int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ |
| 218105 | sqlite3_session *pSession, /* Session object pTab is attached to */ |
| 218106 | SessionTable *pTab /* Table that change applies to */ |
| 218107 | ){ |
| 218108 | int iHash; |
| 218109 | int bNull = 0; |
| @@ -218115,11 +219104,11 @@ | |
| 218115 | /* Load table details if required */ |
| 218116 | if( sessionInitTable(pSession, pTab) ) return; |
| 218117 | |
| 218118 | /* Check the number of columns in this xPreUpdate call matches the |
| 218119 | ** number of columns in the table. */ |
| 218120 | if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){ |
| 218121 | pSession->rc = SQLITE_SCHEMA; |
| 218122 | return; |
| 218123 | } |
| 218124 | |
| 218125 | /* Grow the hash table if required */ |
| @@ -218148,18 +219137,20 @@ | |
| 218148 | } |
| 218149 | |
| 218150 | /* Calculate the hash-key for this change. If the primary key of the row |
| 218151 | ** includes a NULL value, exit early. Such changes are ignored by the |
| 218152 | ** session module. */ |
| 218153 | rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull); |
| 218154 | if( rc!=SQLITE_OK ) goto error_out; |
| 218155 | |
| 218156 | if( bNull==0 ){ |
| 218157 | /* Search the hash table for an existing record for this row. */ |
| 218158 | SessionChange *pC; |
| 218159 | for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ |
| 218160 | if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break; |
| 218161 | } |
| 218162 | |
| 218163 | if( pC==0 ){ |
| 218164 | /* Create a new change object containing all the old values (if |
| 218165 | ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK |
| @@ -218170,11 +219161,11 @@ | |
| 218170 | assert( rc==SQLITE_OK ); |
| 218171 | pTab->nEntry++; |
| 218172 | |
| 218173 | /* Figure out how large an allocation is required */ |
| 218174 | nByte = sizeof(SessionChange); |
| 218175 | for(i=0; i<pTab->nCol; i++){ |
| 218176 | sqlite3_value *p = 0; |
| 218177 | if( op!=SQLITE_INSERT ){ |
| 218178 | TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 218179 | assert( trc==SQLITE_OK ); |
| 218180 | }else if( pTab->abPK[i] ){ |
| @@ -218185,10 +219176,13 @@ | |
| 218185 | /* This may fail if SQLite value p contains a utf-16 string that must |
| 218186 | ** be converted to utf-8 and an OOM error occurs while doing so. */ |
| 218187 | rc = sessionSerializeValue(0, p, &nByte); |
| 218188 | if( rc!=SQLITE_OK ) goto error_out; |
| 218189 | } |
| 218190 | |
| 218191 | /* Allocate the change object */ |
| 218192 | pC = (SessionChange *)sessionMalloc64(pSession, nByte); |
| 218193 | if( !pC ){ |
| 218194 | rc = SQLITE_NOMEM; |
| @@ -218201,11 +219195,16 @@ | |
| 218201 | /* Populate the change object. None of the preupdate_old(), |
| 218202 | ** preupdate_new() or SerializeValue() calls below may fail as all |
| 218203 | ** required values and encodings have already been cached in memory. |
| 218204 | ** It is not possible for an OOM to occur in this block. */ |
| 218205 | nByte = 0; |
| 218206 | for(i=0; i<pTab->nCol; i++){ |
| 218207 | sqlite3_value *p = 0; |
| 218208 | if( op!=SQLITE_INSERT ){ |
| 218209 | pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 218210 | }else if( pTab->abPK[i] ){ |
| 218211 | pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| @@ -218316,13 +219315,14 @@ | |
| 218316 | if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue; |
| 218317 | |
| 218318 | pSession->rc = sessionFindTable(pSession, zName, &pTab); |
| 218319 | if( pTab ){ |
| 218320 | assert( pSession->rc==SQLITE_OK ); |
| 218321 | sessionPreupdateOneChange(op, pSession, pTab); |
| 218322 | if( op==SQLITE_UPDATE ){ |
| 218323 | sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab); |
| 218324 | } |
| 218325 | } |
| 218326 | } |
| 218327 | } |
| 218328 | |
| @@ -218357,29 +219357,30 @@ | |
| 218357 | } |
| 218358 | |
| 218359 | typedef struct SessionDiffCtx SessionDiffCtx; |
| 218360 | struct SessionDiffCtx { |
| 218361 | sqlite3_stmt *pStmt; |
| 218362 | int nOldOff; |
| 218363 | }; |
| 218364 | |
| 218365 | /* |
| 218366 | ** The diff hook implementations. |
| 218367 | */ |
| 218368 | static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 218369 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218370 | *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff); |
| 218371 | return SQLITE_OK; |
| 218372 | } |
| 218373 | static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 218374 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218375 | *ppVal = sqlite3_column_value(p->pStmt, iVal); |
| 218376 | return SQLITE_OK; |
| 218377 | } |
| 218378 | static int sessionDiffCount(void *pCtx){ |
| 218379 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 218380 | return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt); |
| 218381 | } |
| 218382 | static int sessionDiffDepth(void *pCtx){ |
| 218383 | (void)pCtx; |
| 218384 | return 0; |
| 218385 | } |
| @@ -218454,18 +219455,20 @@ | |
| 218454 | } |
| 218455 | |
| 218456 | static char *sessionSelectFindNew( |
| 218457 | const char *zDb1, /* Pick rows in this db only */ |
| 218458 | const char *zDb2, /* But not in this one */ |
| 218459 | const char *zTbl, /* Table name */ |
| 218460 | const char *zExpr |
| 218461 | ){ |
| 218462 | char *zRet = sqlite3_mprintf( |
| 218463 | "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS (" |
| 218464 | " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" |
| 218465 | ")", |
| 218466 | zDb1, zTbl, zDb2, zTbl, zExpr |
| 218467 | ); |
| 218468 | return zRet; |
| 218469 | } |
| 218470 | |
| 218471 | static int sessionDiffFindNew( |
| @@ -218475,11 +219478,13 @@ | |
| 218475 | const char *zDb1, |
| 218476 | const char *zDb2, |
| 218477 | char *zExpr |
| 218478 | ){ |
| 218479 | int rc = SQLITE_OK; |
| 218480 | char *zStmt = sessionSelectFindNew(zDb1, zDb2, pTab->zName,zExpr); |
| 218481 | |
| 218482 | if( zStmt==0 ){ |
| 218483 | rc = SQLITE_NOMEM; |
| 218484 | }else{ |
| 218485 | sqlite3_stmt *pStmt; |
| @@ -218486,20 +219491,43 @@ | |
| 218486 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 218487 | if( rc==SQLITE_OK ){ |
| 218488 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 218489 | pDiffCtx->pStmt = pStmt; |
| 218490 | pDiffCtx->nOldOff = 0; |
| 218491 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218492 | sessionPreupdateOneChange(op, pSession, pTab); |
| 218493 | } |
| 218494 | rc = sqlite3_finalize(pStmt); |
| 218495 | } |
| 218496 | sqlite3_free(zStmt); |
| 218497 | } |
| 218498 | |
| 218499 | return rc; |
| 218500 | } |
| 218501 | |
| 218502 | static int sessionDiffFindModified( |
| 218503 | sqlite3_session *pSession, |
| 218504 | SessionTable *pTab, |
| 218505 | const char *zFrom, |
| @@ -218511,15 +219539,17 @@ | |
| 218511 | pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK |
| 218512 | ); |
| 218513 | if( zExpr2==0 ){ |
| 218514 | rc = SQLITE_NOMEM; |
| 218515 | }else{ |
| 218516 | char *zStmt = sqlite3_mprintf( |
| 218517 | "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", |
| 218518 | pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 |
| 218519 | ); |
| 218520 | if( zStmt==0 ){ |
| 218521 | rc = SQLITE_NOMEM; |
| 218522 | }else{ |
| 218523 | sqlite3_stmt *pStmt; |
| 218524 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 218525 | |
| @@ -218526,16 +219556,19 @@ | |
| 218526 | if( rc==SQLITE_OK ){ |
| 218527 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 218528 | pDiffCtx->pStmt = pStmt; |
| 218529 | pDiffCtx->nOldOff = pTab->nCol; |
| 218530 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218531 | sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab); |
| 218532 | } |
| 218533 | rc = sqlite3_finalize(pStmt); |
| 218534 | } |
| 218535 | sqlite3_free(zStmt); |
| 218536 | } |
| 218537 | } |
| 218538 | |
| 218539 | return rc; |
| 218540 | } |
| 218541 | |
| @@ -218570,13 +219603,16 @@ | |
| 218570 | /* Check the table schemas match */ |
| 218571 | if( rc==SQLITE_OK ){ |
| 218572 | int bHasPk = 0; |
| 218573 | int bMismatch = 0; |
| 218574 | int nCol; /* Columns in zFrom.zTbl */ |
| 218575 | u8 *abPK; |
| 218576 | const char **azCol = 0; |
| 218577 | rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK); |
| 218578 | if( rc==SQLITE_OK ){ |
| 218579 | if( pTo->nCol!=nCol ){ |
| 218580 | bMismatch = 1; |
| 218581 | }else{ |
| 218582 | int i; |
| @@ -219121,11 +220157,11 @@ | |
| 219121 | } |
| 219122 | |
| 219123 | /* If at least one field has been modified, this is not a no-op. */ |
| 219124 | if( bChanged ) bNoop = 0; |
| 219125 | |
| 219126 | /* Add a field to the old.* record. This is omitted if this modules is |
| 219127 | ** currently generating a patchset. */ |
| 219128 | if( bPatchset==0 ){ |
| 219129 | if( bChanged || abPK[i] ){ |
| 219130 | sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); |
| 219131 | }else{ |
| @@ -219223,19 +220259,20 @@ | |
| 219223 | static int sessionSelectStmt( |
| 219224 | sqlite3 *db, /* Database handle */ |
| 219225 | int bIgnoreNoop, |
| 219226 | const char *zDb, /* Database name */ |
| 219227 | const char *zTab, /* Table name */ |
| 219228 | int nCol, /* Number of columns in table */ |
| 219229 | const char **azCol, /* Names of table columns */ |
| 219230 | u8 *abPK, /* PRIMARY KEY array */ |
| 219231 | sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */ |
| 219232 | ){ |
| 219233 | int rc = SQLITE_OK; |
| 219234 | char *zSql = 0; |
| 219235 | const char *zSep = ""; |
| 219236 | const char *zCols = "*"; |
| 219237 | int nSql = -1; |
| 219238 | int i; |
| 219239 | |
| 219240 | SessionBuffer nooptest = {0, 0, 0}; |
| 219241 | SessionBuffer pkfield = {0, 0, 0}; |
| @@ -219250,11 +220287,10 @@ | |
| 219250 | "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc |
| 219251 | ); |
| 219252 | zCols = "tbl, ?2, stat"; |
| 219253 | }else{ |
| 219254 | for(i=0; i<nCol; i++){ |
| 219255 | |
| 219256 | if( abPK[i] ){ |
| 219257 | sessionAppendStr(&pkfield, zSep, &rc); |
| 219258 | sessionAppendStr(&pkvar, zSep, &rc); |
| 219259 | zSep = ", "; |
| 219260 | sessionAppendIdent(&pkfield, azCol[i], &rc); |
| @@ -219457,24 +220493,32 @@ | |
| 219457 | const char **azCol = 0; /* Table columns */ |
| 219458 | int i; /* Used to iterate through hash buckets */ |
| 219459 | sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ |
| 219460 | int nRewind = buf.nBuf; /* Initial size of write buffer */ |
| 219461 | int nNoop; /* Size of buffer after writing tbl header */ |
| 219462 | |
| 219463 | /* Check the table schema is still Ok. */ |
| 219464 | rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK); |
| 219465 | if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){ |
| 219466 | rc = SQLITE_SCHEMA; |
| 219467 | } |
| 219468 | |
| 219469 | /* Write a table header */ |
| 219470 | sessionAppendTableHdr(&buf, bPatchset, pTab, &rc); |
| 219471 | |
| 219472 | /* Build and compile a statement to execute: */ |
| 219473 | if( rc==SQLITE_OK ){ |
| 219474 | rc = sessionSelectStmt( |
| 219475 | db, 0, pSession->zDb, zName, nCol, azCol, abPK, &pSel |
| 219476 | ); |
| 219477 | } |
| 219478 | |
| 219479 | nNoop = buf.nBuf; |
| 219480 | for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){ |
| @@ -219554,11 +220598,11 @@ | |
| 219554 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 219555 | ){ |
| 219556 | int rc; |
| 219557 | |
| 219558 | if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; |
| 219559 | rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset,ppChangeset); |
| 219560 | assert( rc || pnChangeset==0 |
| 219561 | || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize |
| 219562 | ); |
| 219563 | return rc; |
| 219564 | } |
| @@ -219671,10 +220715,23 @@ | |
| 219671 | } |
| 219672 | } |
| 219673 | *(int*)pArg = pSession->bEnableSize; |
| 219674 | break; |
| 219675 | } |
| 219676 | |
| 219677 | default: |
| 219678 | rc = SQLITE_MISUSE; |
| 219679 | } |
| 219680 | |
| @@ -220661,10 +221718,11 @@ | |
| 220661 | SessionBuffer constraints; /* Deferred constraints are stored here */ |
| 220662 | SessionBuffer rebase; /* Rebase information (if any) here */ |
| 220663 | u8 bRebaseStarted; /* If table header is already in rebase */ |
| 220664 | u8 bRebase; /* True to collect rebase information */ |
| 220665 | u8 bIgnoreNoop; /* True to ignore no-op conflicts */ |
| 220666 | }; |
| 220667 | |
| 220668 | /* Number of prepared UPDATE statements to cache. */ |
| 220669 | #define SESSION_UPDATE_CACHE_SZ 12 |
| 220670 | |
| @@ -220911,12 +221969,13 @@ | |
| 220911 | static int sessionSelectRow( |
| 220912 | sqlite3 *db, /* Database handle */ |
| 220913 | const char *zTab, /* Table name */ |
| 220914 | SessionApplyCtx *p /* Session changeset-apply context */ |
| 220915 | ){ |
| 220916 | return sessionSelectStmt(db, p->bIgnoreNoop, |
| 220917 | "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect |
| 220918 | ); |
| 220919 | } |
| 220920 | |
| 220921 | /* |
| 220922 | ** Formulate and prepare an INSERT statement to add a record to table zTab. |
| @@ -221608,10 +222667,11 @@ | |
| 221608 | sApply.azCol = 0; |
| 221609 | sApply.abPK = 0; |
| 221610 | sApply.bStat1 = 0; |
| 221611 | sApply.bDeferConstraints = 1; |
| 221612 | sApply.bRebaseStarted = 0; |
| 221613 | memset(&sApply.constraints, 0, sizeof(SessionBuffer)); |
| 221614 | |
| 221615 | /* If an xFilter() callback was specified, invoke it now. If the |
| 221616 | ** xFilter callback returns zero, skip this table. If it returns |
| 221617 | ** non-zero, proceed. */ |
| @@ -221627,12 +222687,12 @@ | |
| 221627 | }else{ |
| 221628 | int nMinCol = 0; |
| 221629 | int i; |
| 221630 | |
| 221631 | sqlite3changeset_pk(pIter, &abPK, 0); |
| 221632 | rc = sessionTableInfo(0, |
| 221633 | db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK |
| 221634 | ); |
| 221635 | if( rc!=SQLITE_OK ) break; |
| 221636 | for(i=0; i<sApply.nCol; i++){ |
| 221637 | if( sApply.abPK[i] ) nMinCol = i+1; |
| 221638 | } |
| @@ -223510,29 +224570,34 @@ | |
| 223510 | fts5_tokenizer *pTokApi; |
| 223511 | int bLock; /* True when table is preparing statement */ |
| 223512 | int ePattern; /* FTS_PATTERN_XXX constant */ |
| 223513 | |
| 223514 | /* Values loaded from the %_config table */ |
| 223515 | int iCookie; /* Incremented when %_config is modified */ |
| 223516 | int pgsz; /* Approximate page size used in %_data */ |
| 223517 | int nAutomerge; /* 'automerge' setting */ |
| 223518 | int nCrisisMerge; /* Maximum allowed segments per level */ |
| 223519 | int nUsermerge; /* 'usermerge' setting */ |
| 223520 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 223521 | char *zRank; /* Name of rank function */ |
| 223522 | char *zRankArgs; /* Arguments to rank function */ |
| 223523 | |
| 223524 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 223525 | char **pzErrmsg; |
| 223526 | |
| 223527 | #ifdef SQLITE_DEBUG |
| 223528 | int bPrefixIndex; /* True to use prefix-indexes */ |
| 223529 | #endif |
| 223530 | }; |
| 223531 | |
| 223532 | /* Current expected value of %_config table 'version' field */ |
| 223533 | #define FTS5_CURRENT_VERSION 4 |
| 223534 | |
| 223535 | #define FTS5_CONTENT_NORMAL 0 |
| 223536 | #define FTS5_CONTENT_NONE 1 |
| 223537 | #define FTS5_CONTENT_EXTERNAL 2 |
| 223538 | |
| @@ -223694,10 +224759,11 @@ | |
| 223694 | /* The following are used internally by the fts5_index.c module. They are |
| 223695 | ** defined here only to make it easier to avoid clashes with the flags |
| 223696 | ** above. */ |
| 223697 | #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 |
| 223698 | #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 |
| 223699 | |
| 223700 | /* |
| 223701 | ** Create/destroy an Fts5Index object. |
| 223702 | */ |
| 223703 | static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); |
| @@ -223848,11 +224914,11 @@ | |
| 223848 | static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v); |
| 223849 | static int sqlite3Fts5GetVarintLen(u32 iVal); |
| 223850 | static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); |
| 223851 | static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); |
| 223852 | |
| 223853 | #define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b) |
| 223854 | #define fts5GetVarint sqlite3Fts5GetVarint |
| 223855 | |
| 223856 | #define fts5FastGetVarint32(a, iOff, nVal) { \ |
| 223857 | nVal = (a)[iOff++]; \ |
| 223858 | if( nVal & 0x80 ){ \ |
| @@ -227340,10 +228406,11 @@ | |
| 227340 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 227341 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| 227342 | rc = SQLITE_ERROR; |
| 227343 | } |
| 227344 | |
| 227345 | for(i=3; rc==SQLITE_OK && i<nArg; i++){ |
| 227346 | const char *zOrig = azArg[i]; |
| 227347 | const char *z; |
| 227348 | char *zOne = 0; |
| 227349 | char *zTwo = 0; |
| @@ -227693,10 +228760,22 @@ | |
| 227693 | pConfig->zRankArgs = zRankArgs; |
| 227694 | }else if( rc==SQLITE_ERROR ){ |
| 227695 | rc = SQLITE_OK; |
| 227696 | *pbBadkey = 1; |
| 227697 | } |
| 227698 | }else{ |
| 227699 | *pbBadkey = 1; |
| 227700 | } |
| 227701 | return rc; |
| 227702 | } |
| @@ -227737,19 +228816,24 @@ | |
| 227737 | } |
| 227738 | } |
| 227739 | rc = sqlite3_finalize(p); |
| 227740 | } |
| 227741 | |
| 227742 | if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){ |
| 227743 | rc = SQLITE_ERROR; |
| 227744 | if( pConfig->pzErrmsg ){ |
| 227745 | assert( 0==*pConfig->pzErrmsg ); |
| 227746 | *pConfig->pzErrmsg = sqlite3_mprintf( |
| 227747 | "invalid fts5 file format (found %d, expected %d) - run 'rebuild'", |
| 227748 | iVersion, FTS5_CURRENT_VERSION |
| 227749 | ); |
| 227750 | } |
| 227751 | } |
| 227752 | |
| 227753 | if( rc==SQLITE_OK ){ |
| 227754 | pConfig->iCookie = iCookie; |
| 227755 | } |
| @@ -227772,10 +228856,14 @@ | |
| 227772 | |
| 227773 | |
| 227774 | |
| 227775 | /* #include "fts5Int.h" */ |
| 227776 | /* #include "fts5parse.h" */ |
| 227777 | |
| 227778 | /* |
| 227779 | ** All token types in the generated fts5parse.h file are greater than 0. |
| 227780 | */ |
| 227781 | #define FTS5_EOF 0 |
| @@ -227813,15 +228901,21 @@ | |
| 227813 | ** FTS5_AND (nChild, apChild valid) |
| 227814 | ** FTS5_OR (nChild, apChild valid) |
| 227815 | ** FTS5_NOT (nChild, apChild valid) |
| 227816 | ** FTS5_STRING (pNear valid) |
| 227817 | ** FTS5_TERM (pNear valid) |
| 227818 | */ |
| 227819 | struct Fts5ExprNode { |
| 227820 | int eType; /* Node type */ |
| 227821 | int bEof; /* True at EOF */ |
| 227822 | int bNomatch; /* True if entry is not a match */ |
| 227823 | |
| 227824 | /* Next method for this node. */ |
| 227825 | int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); |
| 227826 | |
| 227827 | i64 iRowid; /* Current rowid */ |
| @@ -227887,10 +228981,35 @@ | |
| 227887 | Fts5ExprPhrase **apPhrase; /* Array of all phrases */ |
| 227888 | Fts5ExprNode *pExpr; /* Result of a successful parse */ |
| 227889 | int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ |
| 227890 | }; |
| 227891 | |
| 227892 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ |
| 227893 | va_list ap; |
| 227894 | va_start(ap, zFmt); |
| 227895 | if( pParse->rc==SQLITE_OK ){ |
| 227896 | assert( pParse->zErr==0 ); |
| @@ -228000,10 +229119,12 @@ | |
| 228000 | do { |
| 228001 | t = fts5ExprGetToken(&sParse, &z, &token); |
| 228002 | sqlite3Fts5Parser(pEngine, t, token, &sParse); |
| 228003 | }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); |
| 228004 | sqlite3Fts5ParserFree(pEngine, fts5ParseFree); |
| 228005 | |
| 228006 | /* If the LHS of the MATCH expression was a user column, apply the |
| 228007 | ** implicit column-filter. */ |
| 228008 | if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ |
| 228009 | int n = sizeof(Fts5Colset); |
| @@ -228163,11 +229284,11 @@ | |
| 228163 | |
| 228164 | static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ |
| 228165 | Fts5Parse sParse; |
| 228166 | memset(&sParse, 0, sizeof(sParse)); |
| 228167 | |
| 228168 | if( *pp1 ){ |
| 228169 | Fts5Expr *p1 = *pp1; |
| 228170 | int nPhrase = p1->nPhrase + p2->nPhrase; |
| 228171 | |
| 228172 | p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0); |
| 228173 | p2->pRoot = 0; |
| @@ -228188,11 +229309,11 @@ | |
| 228188 | p1->apExprPhrase = ap; |
| 228189 | } |
| 228190 | } |
| 228191 | sqlite3_free(p2->apExprPhrase); |
| 228192 | sqlite3_free(p2); |
| 228193 | }else{ |
| 228194 | *pp1 = p2; |
| 228195 | } |
| 228196 | |
| 228197 | return sParse.rc; |
| 228198 | } |
| @@ -229962,18 +231083,22 @@ | |
| 229962 | }; |
| 229963 | } |
| 229964 | } |
| 229965 | |
| 229966 | static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ |
| 229967 | if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ |
| 229968 | int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; |
| 229969 | memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); |
| 229970 | p->nChild += pSub->nChild; |
| 229971 | sqlite3_free(pSub); |
| 229972 | }else{ |
| 229973 | p->apChild[p->nChild++] = pSub; |
| 229974 | } |
| 229975 | } |
| 229976 | |
| 229977 | /* |
| 229978 | ** This function is used when parsing LIKE or GLOB patterns against |
| 229979 | ** trigram indexes that specify either detail=column or detail=none. |
| @@ -230000,10 +231125,11 @@ | |
| 230000 | nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 230001 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 230002 | if( pRet ){ |
| 230003 | pRet->eType = FTS5_AND; |
| 230004 | pRet->nChild = nTerm; |
| 230005 | fts5ExprAssignXNext(pRet); |
| 230006 | pParse->nPhrase--; |
| 230007 | for(ii=0; ii<nTerm; ii++){ |
| 230008 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 230009 | &pParse->rc, sizeof(Fts5ExprPhrase) |
| @@ -230105,10 +231231,18 @@ | |
| 230105 | } |
| 230106 | } |
| 230107 | }else{ |
| 230108 | fts5ExprAddChildren(pRet, pLeft); |
| 230109 | fts5ExprAddChildren(pRet, pRight); |
| 230110 | } |
| 230111 | } |
| 230112 | } |
| 230113 | } |
| 230114 | |
| @@ -231705,10 +232839,12 @@ | |
| 231705 | sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ |
| 231706 | sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ |
| 231707 | sqlite3_stmt *pIdxSelect; |
| 231708 | int nRead; /* Total number of blocks read */ |
| 231709 | |
| 231710 | sqlite3_stmt *pDataVersion; |
| 231711 | i64 iStructVersion; /* data_version when pStruct read */ |
| 231712 | Fts5Structure *pStruct; /* Current db structure (or NULL) */ |
| 231713 | }; |
| 231714 | |
| @@ -231797,13 +232933,10 @@ | |
| 231797 | ** Current leaf page number within segment. |
| 231798 | ** |
| 231799 | ** iLeafOffset: |
| 231800 | ** Byte offset within the current leaf that is the first byte of the |
| 231801 | ** position list data (one byte passed the position-list size field). |
| 231802 | ** rowid field of the current entry. Usually this is the size field of the |
| 231803 | ** position list data. The exception is if the rowid for the current entry |
| 231804 | ** is the last thing on the leaf page. |
| 231805 | ** |
| 231806 | ** pLeaf: |
| 231807 | ** Buffer containing current leaf page data. Set to NULL at EOF. |
| 231808 | ** |
| 231809 | ** iTermLeafPgno, iTermLeafOffset: |
| @@ -232358,10 +233491,11 @@ | |
| 232358 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; |
| 232359 | if( i>=nData ){ |
| 232360 | rc = FTS5_CORRUPT; |
| 232361 | break; |
| 232362 | } |
| 232363 | i += fts5GetVarint32(&pData[i], pSeg->iSegid); |
| 232364 | i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); |
| 232365 | i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); |
| 232366 | if( pSeg->pgnoLast<pSeg->pgnoFirst ){ |
| 232367 | rc = FTS5_CORRUPT; |
| @@ -232388,10 +233522,11 @@ | |
| 232388 | ** Add a level to the Fts5Structure.aLevel[] array of structure object |
| 232389 | ** (*ppStruct). |
| 232390 | */ |
| 232391 | static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ |
| 232392 | fts5StructureMakeWritable(pRc, ppStruct); |
| 232393 | if( *pRc==SQLITE_OK ){ |
| 232394 | Fts5Structure *pStruct = *ppStruct; |
| 232395 | int nLevel = pStruct->nLevel; |
| 232396 | sqlite3_int64 nByte = ( |
| 232397 | sizeof(Fts5Structure) + /* Main structure */ |
| @@ -232846,46 +233981,29 @@ | |
| 232846 | assert( pLvl->bEof==0 ); |
| 232847 | if( iOff<=pLvl->iFirstOff ){ |
| 232848 | pLvl->bEof = 1; |
| 232849 | }else{ |
| 232850 | u8 *a = pLvl->pData->p; |
| 232851 | i64 iVal; |
| 232852 | int iLimit; |
| 232853 | int ii; |
| 232854 | int nZero = 0; |
| 232855 | |
| 232856 | /* Currently iOff points to the first byte of a varint. This block |
| 232857 | ** decrements iOff until it points to the first byte of the previous |
| 232858 | ** varint. Taking care not to read any memory locations that occur |
| 232859 | ** before the buffer in memory. */ |
| 232860 | iLimit = (iOff>9 ? iOff-9 : 0); |
| 232861 | for(iOff--; iOff>iLimit; iOff--){ |
| 232862 | if( (a[iOff-1] & 0x80)==0 ) break; |
| 232863 | } |
| 232864 | |
| 232865 | fts5GetVarint(&a[iOff], (u64*)&iVal); |
| 232866 | pLvl->iRowid -= iVal; |
| 232867 | pLvl->iLeafPgno--; |
| 232868 | |
| 232869 | /* Skip backwards past any 0x00 varints. */ |
| 232870 | for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){ |
| 232871 | nZero++; |
| 232872 | } |
| 232873 | if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){ |
| 232874 | /* The byte immediately before the last 0x00 byte has the 0x80 bit |
| 232875 | ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80 |
| 232876 | ** bytes before a[ii]. */ |
| 232877 | int bZero = 0; /* True if last 0x00 counts */ |
| 232878 | if( (ii-8)>=pLvl->iFirstOff ){ |
| 232879 | int j; |
| 232880 | for(j=1; j<=8 && (a[ii-j] & 0x80); j++); |
| 232881 | bZero = (j>8); |
| 232882 | } |
| 232883 | if( bZero==0 ) nZero--; |
| 232884 | } |
| 232885 | pLvl->iLeafPgno -= nZero; |
| 232886 | pLvl->iOff = iOff - nZero; |
| 232887 | } |
| 232888 | |
| 232889 | return pLvl->bEof; |
| 232890 | } |
| 232891 | |
| @@ -233077,11 +234195,11 @@ | |
| 233077 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 233078 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| 233079 | i64 iOff = pIter->iLeafOffset; |
| 233080 | |
| 233081 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 233082 | if( iOff>=pIter->pLeaf->szLeaf ){ |
| 233083 | fts5SegIterNextPage(p, pIter); |
| 233084 | if( pIter->pLeaf==0 ){ |
| 233085 | if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; |
| 233086 | return; |
| 233087 | } |
| @@ -233176,14 +234294,16 @@ | |
| 233176 | if( p->rc==SQLITE_OK ){ |
| 233177 | memset(pIter, 0, sizeof(*pIter)); |
| 233178 | fts5SegIterSetNext(p, pIter); |
| 233179 | pIter->pSeg = pSeg; |
| 233180 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 233181 | fts5SegIterNextPage(p, pIter); |
| 233182 | } |
| 233183 | |
| 233184 | if( p->rc==SQLITE_OK ){ |
| 233185 | pIter->iLeafOffset = 4; |
| 233186 | assert( pIter->pLeaf!=0 ); |
| 233187 | assert_nc( pIter->pLeaf->nn>4 ); |
| 233188 | assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); |
| 233189 | pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; |
| @@ -233373,11 +234493,11 @@ | |
| 233373 | |
| 233374 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 233375 | iOff = pIter->iLeafOffset; |
| 233376 | |
| 233377 | /* Next entry is on the next page */ |
| 233378 | if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ |
| 233379 | fts5SegIterNextPage(p, pIter); |
| 233380 | if( p->rc || pIter->pLeaf==0 ) return; |
| 233381 | pIter->iRowid = 0; |
| 233382 | iOff = 4; |
| 233383 | } |
| @@ -233566,11 +234686,11 @@ | |
| 233566 | static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ |
| 233567 | Fts5DlidxIter *pDlidx = pIter->pDlidx; |
| 233568 | Fts5Data *pLast = 0; |
| 233569 | int pgnoLast = 0; |
| 233570 | |
| 233571 | if( pDlidx ){ |
| 233572 | int iSegid = pIter->pSeg->iSegid; |
| 233573 | pgnoLast = fts5DlidxIterPgno(pDlidx); |
| 233574 | pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); |
| 233575 | }else{ |
| 233576 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| @@ -234127,11 +235247,12 @@ | |
| 234127 | return 0; |
| 234128 | } |
| 234129 | |
| 234130 | /* |
| 234131 | ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. |
| 234132 | ** It is an error if leaf iLeafPgno does not exist or contains no rowids. |
| 234133 | */ |
| 234134 | static void fts5SegIterGotoPage( |
| 234135 | Fts5Index *p, /* FTS5 backend object */ |
| 234136 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 234137 | int iLeafPgno |
| @@ -234142,25 +235263,27 @@ | |
| 234142 | p->rc = FTS5_CORRUPT; |
| 234143 | }else{ |
| 234144 | fts5DataRelease(pIter->pNextLeaf); |
| 234145 | pIter->pNextLeaf = 0; |
| 234146 | pIter->iLeafPgno = iLeafPgno-1; |
| 234147 | fts5SegIterNextPage(p, pIter); |
| 234148 | assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); |
| 234149 | |
| 234150 | if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){ |
| 234151 | int iOff; |
| 234152 | u8 *a = pIter->pLeaf->p; |
| 234153 | int n = pIter->pLeaf->szLeaf; |
| 234154 | |
| 234155 | iOff = fts5LeafFirstRowidOff(pIter->pLeaf); |
| 234156 | if( iOff<4 || iOff>=n ){ |
| 234157 | p->rc = FTS5_CORRUPT; |
| 234158 | }else{ |
| 234159 | iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); |
| 234160 | pIter->iLeafOffset = iOff; |
| 234161 | fts5SegIterLoadNPos(p, pIter); |
| 234162 | } |
| 234163 | } |
| 234164 | } |
| 234165 | } |
| 234166 | |
| @@ -234871,11 +235994,11 @@ | |
| 234871 | /* Allocate space for the new multi-seg-iterator. */ |
| 234872 | if( p->rc==SQLITE_OK ){ |
| 234873 | if( iLevel<0 ){ |
| 234874 | assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); |
| 234875 | nSeg = pStruct->nSegment; |
| 234876 | nSeg += (p->pHash ? 1 : 0); |
| 234877 | }else{ |
| 234878 | nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); |
| 234879 | } |
| 234880 | } |
| 234881 | *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); |
| @@ -234892,11 +236015,11 @@ | |
| 234892 | |
| 234893 | /* Initialize each of the component segment iterators. */ |
| 234894 | if( p->rc==SQLITE_OK ){ |
| 234895 | if( iLevel<0 ){ |
| 234896 | Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; |
| 234897 | if( p->pHash ){ |
| 234898 | /* Add a segment iterator for the current contents of the hash table. */ |
| 234899 | Fts5SegIter *pIter = &pNew->aSeg[iIter++]; |
| 234900 | fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); |
| 234901 | } |
| 234902 | for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ |
| @@ -235647,11 +236770,11 @@ | |
| 235647 | fts5BufferZero(&buf); |
| 235648 | fts5BufferGrow(&p->rc, &buf, pData->nn); |
| 235649 | fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); |
| 235650 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); |
| 235651 | fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); |
| 235652 | fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); |
| 235653 | if( p->rc==SQLITE_OK ){ |
| 235654 | /* Set the szLeaf field */ |
| 235655 | fts5PutU16(&buf.p[2], (u16)buf.n); |
| 235656 | } |
| 235657 | |
| @@ -235925,20 +237048,20 @@ | |
| 235925 | Fts5Index *p, /* FTS5 backend object */ |
| 235926 | Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ |
| 235927 | ){ |
| 235928 | const int nCrisis = p->pConfig->nCrisisMerge; |
| 235929 | Fts5Structure *pStruct = *ppStruct; |
| 235930 | int iLvl = 0; |
| 235931 | |
| 235932 | assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 ); |
| 235933 | while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ |
| 235934 | fts5IndexMergeLevel(p, &pStruct, iLvl, 0); |
| 235935 | assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); |
| 235936 | fts5StructurePromote(p, iLvl+1, pStruct); |
| 235937 | iLvl++; |
| 235938 | } |
| 235939 | *ppStruct = pStruct; |
| 235940 | } |
| 235941 | |
| 235942 | static int fts5IndexReturn(Fts5Index *p){ |
| 235943 | int rc = p->rc; |
| 235944 | p->rc = SQLITE_OK; |
| @@ -235967,10 +237090,417 @@ | |
| 235967 | ret += i; |
| 235968 | } |
| 235969 | } |
| 235970 | return ret; |
| 235971 | } |
| 235972 | |
| 235973 | /* |
| 235974 | ** Flush the contents of in-memory hash table iHash to a new level-0 |
| 235975 | ** segment on disk. Also update the corresponding structure record. |
| 235976 | ** |
| @@ -235990,10 +237520,11 @@ | |
| 235990 | fts5StructureInvalidate(p); |
| 235991 | |
| 235992 | if( iSegid ){ |
| 235993 | const int pgsz = p->pConfig->pgsz; |
| 235994 | int eDetail = p->pConfig->eDetail; |
| 235995 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 235996 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 235997 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 235998 | |
| 235999 | Fts5SegWriter writer; |
| @@ -236012,44 +237543,81 @@ | |
| 236012 | if( p->rc==SQLITE_OK ){ |
| 236013 | p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); |
| 236014 | } |
| 236015 | while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ |
| 236016 | const char *zTerm; /* Buffer containing term */ |
| 236017 | const u8 *pDoclist; /* Pointer to doclist for this term */ |
| 236018 | int nDoclist; /* Size of doclist in bytes */ |
| 236019 | |
| 236020 | /* Write the term for this entry to disk. */ |
| 236021 | sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); |
| 236022 | fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); |
| 236023 | if( p->rc!=SQLITE_OK ) break; |
| 236024 | |
| 236025 | assert( writer.bFirstRowidInPage==0 ); |
| 236026 | if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ |
| 236027 | /* The entire doclist will fit on the current leaf. */ |
| 236028 | fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); |
| 236029 | }else{ |
| 236030 | i64 iRowid = 0; |
| 236031 | u64 iDelta = 0; |
| 236032 | int iOff = 0; |
| 236033 | |
| 236034 | /* The entire doclist will not fit on this leaf. The following |
| 236035 | ** loop iterates through the poslists that make up the current |
| 236036 | ** doclist. */ |
| 236037 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 236038 | iOff += fts5GetVarint(&pDoclist[iOff], &iDelta); |
| 236039 | iRowid += iDelta; |
| 236040 | |
| 236041 | if( writer.bFirstRowidInPage ){ |
| 236042 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 236043 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| 236044 | writer.bFirstRowidInPage = 0; |
| 236045 | fts5WriteDlidxAppend(p, &writer, iRowid); |
| 236046 | if( p->rc!=SQLITE_OK ) break; |
| 236047 | }else{ |
| 236048 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); |
| 236049 | } |
| 236050 | assert( pBuf->n<=pBuf->nSpace ); |
| 236051 | |
| 236052 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 236053 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ |
| 236054 | pBuf->p[pBuf->n++] = 0; |
| 236055 | iOff++; |
| @@ -236104,24 +237672,27 @@ | |
| 236104 | if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); |
| 236105 | } |
| 236106 | sqlite3Fts5HashClear(pHash); |
| 236107 | fts5WriteFinish(p, &writer, &pgnoLast); |
| 236108 | |
| 236109 | /* Update the Fts5Structure. It is written back to the database by the |
| 236110 | ** fts5StructureRelease() call below. */ |
| 236111 | if( pStruct->nLevel==0 ){ |
| 236112 | fts5StructureAddLevel(&p->rc, &pStruct); |
| 236113 | } |
| 236114 | fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); |
| 236115 | if( p->rc==SQLITE_OK ){ |
| 236116 | pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; |
| 236117 | pSeg->iSegid = iSegid; |
| 236118 | pSeg->pgnoFirst = 1; |
| 236119 | pSeg->pgnoLast = pgnoLast; |
| 236120 | pStruct->nSegment++; |
| 236121 | } |
| 236122 | fts5StructurePromote(p, 0, pStruct); |
| 236123 | } |
| 236124 | |
| 236125 | fts5IndexAutomerge(p, &pStruct, pgnoLast); |
| 236126 | fts5IndexCrisismerge(p, &pStruct); |
| 236127 | fts5StructureWrite(p, pStruct); |
| @@ -236858,10 +238429,11 @@ | |
| 236858 | sqlite3_finalize(p->pDeleter); |
| 236859 | sqlite3_finalize(p->pIdxWriter); |
| 236860 | sqlite3_finalize(p->pIdxDeleter); |
| 236861 | sqlite3_finalize(p->pIdxSelect); |
| 236862 | sqlite3_finalize(p->pDataVersion); |
| 236863 | sqlite3Fts5HashFree(p->pHash); |
| 236864 | sqlite3_free(p->zDataTbl); |
| 236865 | sqlite3_free(p); |
| 236866 | } |
| 236867 | return rc; |
| @@ -237488,10 +239060,11 @@ | |
| 237488 | static void fts5IndexIntegrityCheckSegment( |
| 237489 | Fts5Index *p, /* FTS5 backend object */ |
| 237490 | Fts5StructureSegment *pSeg /* Segment to check internal consistency */ |
| 237491 | ){ |
| 237492 | Fts5Config *pConfig = p->pConfig; |
| 237493 | sqlite3_stmt *pStmt = 0; |
| 237494 | int rc2; |
| 237495 | int iIdxPrevLeaf = pSeg->pgnoFirst-1; |
| 237496 | int iDlidxPrevLeaf = pSeg->pgnoLast; |
| 237497 | |
| @@ -237523,11 +239096,23 @@ | |
| 237523 | /* Check that the leaf contains at least one term, and that it is equal |
| 237524 | ** to or larger than the split-key in zIdxTerm. Also check that if there |
| 237525 | ** is also a rowid pointer within the leaf page header, it points to a |
| 237526 | ** location before the term. */ |
| 237527 | if( pLeaf->nn<=pLeaf->szLeaf ){ |
| 237528 | p->rc = FTS5_CORRUPT; |
| 237529 | }else{ |
| 237530 | int iOff; /* Offset of first term on leaf */ |
| 237531 | int iRowidOff; /* Offset of first rowid on leaf */ |
| 237532 | int nTerm; /* Size of term on leaf in bytes */ |
| 237533 | int res; /* Comparison of term and split-key */ |
| @@ -237587,13 +239172,16 @@ | |
| 237587 | i64 iRowid; |
| 237588 | int iRowidOff = fts5LeafFirstRowidOff(pLeaf); |
| 237589 | ASSERT_SZLEAF_OK(pLeaf); |
| 237590 | if( iRowidOff>=pLeaf->szLeaf ){ |
| 237591 | p->rc = FTS5_CORRUPT; |
| 237592 | }else{ |
| 237593 | fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); |
| 237594 | if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT; |
| 237595 | } |
| 237596 | fts5DataRelease(pLeaf); |
| 237597 | } |
| 237598 | } |
| 237599 | |
| @@ -239851,10 +241439,12 @@ | |
| 239851 | ){ |
| 239852 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 239853 | Fts5Config *pConfig = pTab->p.pConfig; |
| 239854 | int eType0; /* value_type() of apVal[0] */ |
| 239855 | int rc = SQLITE_OK; /* Return code */ |
| 239856 | |
| 239857 | /* A transaction must be open when this is called. */ |
| 239858 | assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); |
| 239859 | |
| 239860 | assert( pVtab->zErrMsg==0 ); |
| @@ -239861,10 +241451,15 @@ | |
| 239861 | assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); |
| 239862 | assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER |
| 239863 | || sqlite3_value_type(apVal[0])==SQLITE_NULL |
| 239864 | ); |
| 239865 | assert( pTab->p.pConfig->pzErrmsg==0 ); |
| 239866 | pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 239867 | |
| 239868 | /* Put any active cursors into REQUIRE_SEEK state. */ |
| 239869 | fts5TripCursors(pTab); |
| 239870 | |
| @@ -239913,10 +241508,11 @@ | |
| 239913 | |
| 239914 | /* DELETE */ |
| 239915 | else if( nArg==1 ){ |
| 239916 | i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
| 239917 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); |
| 239918 | } |
| 239919 | |
| 239920 | /* INSERT or UPDATE */ |
| 239921 | else{ |
| 239922 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| @@ -239928,10 +241524,11 @@ | |
| 239928 | else if( eType0!=SQLITE_INTEGER ){ |
| 239929 | /* If this is a REPLACE, first remove the current entry (if any) */ |
| 239930 | if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ |
| 239931 | i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ |
| 239932 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); |
| 239933 | } |
| 239934 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 239935 | } |
| 239936 | |
| 239937 | /* UPDATE */ |
| @@ -239956,13 +241553,27 @@ | |
| 239956 | } |
| 239957 | }else{ |
| 239958 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
| 239959 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 239960 | } |
| 239961 | } |
| 239962 | } |
| 239963 | } |
| 239964 | |
| 239965 | pTab->p.pConfig->pzErrmsg = 0; |
| 239966 | return rc; |
| 239967 | } |
| 239968 | |
| @@ -240819,10 +242430,11 @@ | |
| 240819 | static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
| 240820 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 240821 | UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
| 240822 | fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); |
| 240823 | fts5TripCursors(pTab); |
| 240824 | return sqlite3Fts5StorageRollback(pTab->pStorage); |
| 240825 | } |
| 240826 | |
| 240827 | /* |
| 240828 | ** Register a new auxiliary function with global context pGlobal. |
| @@ -241021,11 +242633,11 @@ | |
| 241021 | int nArg, /* Number of args */ |
| 241022 | sqlite3_value **apUnused /* Function arguments */ |
| 241023 | ){ |
| 241024 | assert( nArg==0 ); |
| 241025 | UNUSED_PARAM2(nArg, apUnused); |
| 241026 | sqlite3_result_text(pCtx, "fts5: 2023-04-10 13:20:51 49ba030080dd00b4fdf788fd3da057b333e705fa0fe37d653e2461bf96ca3785", -1, SQLITE_TRANSIENT); |
| 241027 | } |
| 241028 | |
| 241029 | /* |
| 241030 | ** Return true if zName is the extension on one of the shadow tables used |
| 241031 | ** by this module. |
| 241032 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -121,10 +121,14 @@ | |
| 121 | #if defined(_MSC_VER) && !defined(_WIN64) |
| 122 | #undef SQLITE_4_BYTE_ALIGNED_MALLOC |
| 123 | #define SQLITE_4_BYTE_ALIGNED_MALLOC |
| 124 | #endif /* defined(_MSC_VER) && !defined(_WIN64) */ |
| 125 | |
| 126 | #if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 |
| 127 | #define HAVE_LOG2 0 |
| 128 | #endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */ |
| 129 | |
| 130 | #endif /* SQLITE_MSVC_H */ |
| 131 | |
| 132 | /************** End of msvc.h ************************************************/ |
| 133 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 134 | |
| @@ -452,11 +456,11 @@ | |
| 456 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 457 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 458 | */ |
| 459 | #define SQLITE_VERSION "3.42.0" |
| 460 | #define SQLITE_VERSION_NUMBER 3042000 |
| 461 | #define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" |
| 462 | |
| 463 | /* |
| 464 | ** CAPI3REF: Run-Time Library Version Numbers |
| 465 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 466 | ** |
| @@ -2446,32 +2450,32 @@ | |
| 2450 | ** configuration setting is never used, then the default maximum is determined |
| 2451 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2452 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2453 | ** </dl> |
| 2454 | */ |
| 2455 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 2456 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 2457 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| 2458 | #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ |
| 2459 | #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| 2460 | #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ |
| 2461 | #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ |
| 2462 | #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ |
| 2463 | #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ |
| 2464 | #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ |
| 2465 | #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ |
| 2466 | /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ |
| 2467 | #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ |
| 2468 | #define SQLITE_CONFIG_PCACHE 14 /* no-op */ |
| 2469 | #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ |
| 2470 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ |
| 2471 | #define SQLITE_CONFIG_URI 17 /* int */ |
| 2472 | #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ |
| 2473 | #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ |
| 2474 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2475 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 2476 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 2477 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2478 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2479 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2480 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2481 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2702,29 +2706,29 @@ | |
| 2706 | ** additional information. This feature can also be turned on and off |
| 2707 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2708 | ** </dd> |
| 2709 | ** |
| 2710 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2711 | ** <dt>SQLITE_DBCONFIG_DQS_DML</dt> |
| 2712 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2713 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2714 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2715 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2716 | ** compile-time option. |
| 2717 | ** </dd> |
| 2718 | ** |
| 2719 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2720 | ** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> |
| 2721 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2722 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2723 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2724 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2725 | ** compile-time option. |
| 2726 | ** </dd> |
| 2727 | ** |
| 2728 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2729 | ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> |
| 2730 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2731 | ** assume that database schemas are untainted by malicious content. |
| 2732 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2733 | ** takes additional defensive steps to protect the application from harm |
| 2734 | ** including: |
| @@ -2740,20 +2744,20 @@ | |
| 2744 | ** all applications are advised to turn it off if possible. This setting |
| 2745 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2746 | ** </dd> |
| 2747 | ** |
| 2748 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2749 | ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> |
| 2750 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2751 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2752 | ** created database file to have a schema format version number (the 4-byte |
| 2753 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2754 | ** means that the resulting database file will be readable and writable by |
| 2755 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2756 | ** newly created databases are generally not understandable by SQLite versions |
| 2757 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2758 | ** is now scarcely any need to generate database files that are compatible |
| 2759 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2760 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2761 | ** ability to generate new database files that are compatible with version |
| 2762 | ** 3.0.0. |
| 2763 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2762,27 +2766,39 @@ | |
| 2766 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2767 | ** either generated columns or decending indexes. |
| 2768 | ** </dd> |
| 2769 | ** |
| 2770 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2771 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> |
| 2772 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2773 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2774 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2775 | ** statistics. For statistics to be collected, the flag must be set on |
| 2776 | ** the database handle both when the SQL statement is prepared and when it |
| 2777 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2778 | ** by default. This option takes two arguments: an integer and a pointer to |
| 2779 | ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or |
| 2780 | ** leave unchanged the statement scanstatus option. If the second argument |
| 2781 | ** is not NULL, then the value of the statement scanstatus setting after |
| 2782 | ** processing the first argument is written into the integer that the second |
| 2783 | ** argument points to. |
| 2784 | ** </dd> |
| 2785 | ** |
| 2786 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2787 | ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> |
| 2788 | ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order |
| 2789 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2790 | ** and work toward the beginning rather than starting at the beginning and |
| 2791 | ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the |
| 2792 | ** same as setting [PRAGMA reverse_unordered_selects]. This option takes |
| 2793 | ** two arguments which are an integer and a pointer to an integer. The first |
| 2794 | ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the |
| 2795 | ** reverse scan order flag, respectively. If the second argument is not NULL, |
| 2796 | ** then 0 or 1 is written into the integer that the second argument points to |
| 2797 | ** depending on if the reverse scan order flag is set after processing the |
| 2798 | ** first argument. |
| 2799 | ** </dd> |
| 2800 | ** |
| 2801 | ** </dl> |
| 2802 | */ |
| 2803 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2804 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2800,11 +2816,11 @@ | |
| 2816 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2817 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2818 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2819 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2820 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2821 | #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ |
| 2822 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2823 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2824 | |
| 2825 | /* |
| 2826 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6547,10 +6563,17 @@ | |
| 6563 | ** ^SQLite implements this interface by calling the xSleep() |
| 6564 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6565 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6566 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6567 | ** in the previous paragraphs. |
| 6568 | ** |
| 6569 | ** If a negative argument is passed to sqlite3_sleep() the results vary by |
| 6570 | ** VFS and operating system. Some system treat a negative argument as an |
| 6571 | ** instruction to sleep forever. Others understand it to mean do not sleep |
| 6572 | ** at all. ^In SQLite version 3.42.0 and later, a negative |
| 6573 | ** argument passed into sqlite3_sleep() is changed to zero before it is relayed |
| 6574 | ** down into the xSleep method of the VFS. |
| 6575 | */ |
| 6576 | SQLITE_API int sqlite3_sleep(int); |
| 6577 | |
| 6578 | /* |
| 6579 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -8174,13 +8197,13 @@ | |
| 8197 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 8198 | ** previously entered by the same thread. The behavior |
| 8199 | ** is undefined if the mutex is not currently entered by the |
| 8200 | ** calling thread or is not currently allocated. |
| 8201 | ** |
| 8202 | ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), |
| 8203 | ** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, |
| 8204 | ** then any of the four routines behaves as a no-op. |
| 8205 | ** |
| 8206 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 8207 | */ |
| 8208 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 8209 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -11106,20 +11129,24 @@ | |
| 11129 | ** [sqlite3session_create()] for details. |
| 11130 | */ |
| 11131 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 11132 | |
| 11133 | /* |
| 11134 | ** CAPI3REF: Configure a Session Object |
| 11135 | ** METHOD: sqlite3_session |
| 11136 | ** |
| 11137 | ** This method is used to configure a session object after it has been |
| 11138 | ** created. At present the only valid values for the second parameter are |
| 11139 | ** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. |
| 11140 | ** |
| 11141 | */ |
| 11142 | SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); |
| 11143 | |
| 11144 | /* |
| 11145 | ** CAPI3REF: Options for sqlite3session_object_config |
| 11146 | ** |
| 11147 | ** The following values may passed as the the 2nd parameter to |
| 11148 | ** sqlite3session_object_config(). |
| 11149 | ** |
| 11150 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 11151 | ** This option is used to set, clear or query the flag that enables |
| 11152 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -11131,16 +11158,25 @@ | |
| 11158 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 11159 | ** enabled following the current call, or 0 otherwise. |
| 11160 | ** |
| 11161 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 11162 | ** the first table has been attached to the session object. |
| 11163 | ** |
| 11164 | ** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> |
| 11165 | ** This option is used to set, clear or query the flag that enables |
| 11166 | ** collection of data for tables with no explicit PRIMARY KEY. |
| 11167 | ** |
| 11168 | ** Normally, tables with no explicit PRIMARY KEY are simply ignored |
| 11169 | ** by the sessions module. However, if this flag is set, it behaves |
| 11170 | ** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted |
| 11171 | ** as their leftmost columns. |
| 11172 | ** |
| 11173 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 11174 | ** the first table has been attached to the session object. |
| 11175 | */ |
| 11176 | #define SQLITE_SESSION_OBJCONFIG_SIZE 1 |
| 11177 | #define SQLITE_SESSION_OBJCONFIG_ROWID 2 |
| 11178 | |
| 11179 | /* |
| 11180 | ** CAPI3REF: Enable Or Disable A Session Object |
| 11181 | ** METHOD: sqlite3_session |
| 11182 | ** |
| @@ -13652,11 +13688,11 @@ | |
| 13688 | # define SQLITE_INLINE __forceinline |
| 13689 | #else |
| 13690 | # define SQLITE_NOINLINE |
| 13691 | # define SQLITE_INLINE |
| 13692 | #endif |
| 13693 | #if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) |
| 13694 | # undef SQLITE_INLINE |
| 13695 | # define SQLITE_INLINE |
| 13696 | #endif |
| 13697 | |
| 13698 | /* |
| @@ -18264,10 +18300,11 @@ | |
| 18300 | unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ |
| 18301 | unsigned bHasExpr:1; /* Index contains an expression, either a literal |
| 18302 | ** expression, or a reference to a VIRTUAL column */ |
| 18303 | #ifdef SQLITE_ENABLE_STAT4 |
| 18304 | int nSample; /* Number of elements in aSample[] */ |
| 18305 | int mxSample; /* Number of slots allocated to aSample[] */ |
| 18306 | int nSampleCol; /* Size of IndexSample.anEq[] and so on */ |
| 18307 | tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ |
| 18308 | IndexSample *aSample; /* Samples of the left-most key */ |
| 18309 | tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */ |
| 18310 | tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */ |
| @@ -20020,19 +20057,23 @@ | |
| 20057 | # define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) |
| 20058 | # define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) |
| 20059 | # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) |
| 20060 | # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) |
| 20061 | # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) |
| 20062 | # define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) |
| 20063 | # define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) |
| 20064 | #else |
| 20065 | # define sqlite3Toupper(x) toupper((unsigned char)(x)) |
| 20066 | # define sqlite3Isspace(x) isspace((unsigned char)(x)) |
| 20067 | # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) |
| 20068 | # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) |
| 20069 | # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) |
| 20070 | # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) |
| 20071 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) |
| 20072 | # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') |
| 20073 | # define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') |
| 20074 | # define sqlite3JsonId2(x) sqlite3IsIdChar(x) |
| 20075 | #endif |
| 20076 | SQLITE_PRIVATE int sqlite3IsIdChar(u8); |
| 20077 | |
| 20078 | /* |
| 20079 | ** Internal function prototypes |
| @@ -20465,11 +20506,11 @@ | |
| 20506 | SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); |
| 20507 | SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); |
| 20508 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); |
| 20509 | SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); |
| 20510 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); |
| 20511 | SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); |
| 20512 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 20513 | SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); |
| 20514 | #endif |
| 20515 | SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); |
| 20516 | SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); |
| @@ -22161,11 +22202,11 @@ | |
| 22202 | ** isalpha() 0x02 |
| 22203 | ** isdigit() 0x04 |
| 22204 | ** isalnum() 0x06 |
| 22205 | ** isxdigit() 0x08 |
| 22206 | ** toupper() 0x20 |
| 22207 | ** SQLite identifier character 0x40 $, _, or non-ascii |
| 22208 | ** Quote character 0x80 |
| 22209 | ** |
| 22210 | ** Bit 0x20 is set if the mapped character requires translation to upper |
| 22211 | ** case. i.e. if the character is a lower-case ASCII character. |
| 22212 | ** If x is a lower-case ASCII character, then its upper-case equivalent |
| @@ -23654,10 +23695,11 @@ | |
| 23695 | char validYMD; /* True (1) if Y,M,D are valid */ |
| 23696 | char validHMS; /* True (1) if h,m,s are valid */ |
| 23697 | char validTZ; /* True (1) if tz is valid */ |
| 23698 | char tzSet; /* Timezone was set explicitly */ |
| 23699 | char isError; /* An overflow has occurred */ |
| 23700 | char useSubsec; /* Display subsecond precision */ |
| 23701 | }; |
| 23702 | |
| 23703 | |
| 23704 | /* |
| 23705 | ** Convert zDate into one or more integers according to the conversion |
| @@ -23968,10 +24010,15 @@ | |
| 24010 | }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){ |
| 24011 | return setDateTimeToCurrent(context, p); |
| 24012 | }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ |
| 24013 | setRawDateNumber(p, r); |
| 24014 | return 0; |
| 24015 | }else if( (sqlite3StrICmp(zDate,"subsec")==0 |
| 24016 | || sqlite3StrICmp(zDate,"subsecond")==0) |
| 24017 | && sqlite3NotPureFunc(context) ){ |
| 24018 | p->useSubsec = 1; |
| 24019 | return setDateTimeToCurrent(context, p); |
| 24020 | } |
| 24021 | return 1; |
| 24022 | } |
| 24023 | |
| 24024 | /* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999. |
| @@ -24382,12 +24429,26 @@ | |
| 24429 | /* |
| 24430 | ** start of TTTTT |
| 24431 | ** |
| 24432 | ** Move the date backwards to the beginning of the current day, |
| 24433 | ** or month or year. |
| 24434 | ** |
| 24435 | ** subsecond |
| 24436 | ** subsec |
| 24437 | ** |
| 24438 | ** Show subsecond precision in the output of datetime() and |
| 24439 | ** unixepoch() and strftime('%s'). |
| 24440 | */ |
| 24441 | if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ |
| 24442 | if( sqlite3_stricmp(z, "subsec")==0 |
| 24443 | || sqlite3_stricmp(z, "subsecond")==0 |
| 24444 | ){ |
| 24445 | p->useSubsec = 1; |
| 24446 | rc = 0; |
| 24447 | } |
| 24448 | break; |
| 24449 | } |
| 24450 | if( !p->validJD && !p->validYMD && !p->validHMS ) break; |
| 24451 | z += 9; |
| 24452 | computeYMD(p); |
| 24453 | p->validHMS = 1; |
| 24454 | p->h = p->m = 0; |
| @@ -24581,11 +24642,15 @@ | |
| 24642 | sqlite3_value **argv |
| 24643 | ){ |
| 24644 | DateTime x; |
| 24645 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24646 | computeJD(&x); |
| 24647 | if( x.useSubsec ){ |
| 24648 | sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); |
| 24649 | }else{ |
| 24650 | sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); |
| 24651 | } |
| 24652 | } |
| 24653 | } |
| 24654 | |
| 24655 | /* |
| 24656 | ** datetime( TIMESTRING, MOD, MOD, ...) |
| @@ -24597,12 +24662,12 @@ | |
| 24662 | int argc, |
| 24663 | sqlite3_value **argv |
| 24664 | ){ |
| 24665 | DateTime x; |
| 24666 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24667 | int Y, s, n; |
| 24668 | char zBuf[32]; |
| 24669 | computeYMD_HMS(&x); |
| 24670 | Y = x.Y; |
| 24671 | if( Y<0 ) Y = -Y; |
| 24672 | zBuf[1] = '0' + (Y/1000)%10; |
| 24673 | zBuf[2] = '0' + (Y/100)%10; |
| @@ -24619,19 +24684,32 @@ | |
| 24684 | zBuf[13] = '0' + (x.h)%10; |
| 24685 | zBuf[14] = ':'; |
| 24686 | zBuf[15] = '0' + (x.m/10)%10; |
| 24687 | zBuf[16] = '0' + (x.m)%10; |
| 24688 | zBuf[17] = ':'; |
| 24689 | if( x.useSubsec ){ |
| 24690 | s = (int)1000.0*x.s; |
| 24691 | zBuf[18] = '0' + (s/10000)%10; |
| 24692 | zBuf[19] = '0' + (s/1000)%10; |
| 24693 | zBuf[20] = '.'; |
| 24694 | zBuf[21] = '0' + (s/100)%10; |
| 24695 | zBuf[22] = '0' + (s/10)%10; |
| 24696 | zBuf[23] = '0' + (s)%10; |
| 24697 | zBuf[24] = 0; |
| 24698 | n = 24; |
| 24699 | }else{ |
| 24700 | s = (int)x.s; |
| 24701 | zBuf[18] = '0' + (s/10)%10; |
| 24702 | zBuf[19] = '0' + (s)%10; |
| 24703 | zBuf[20] = 0; |
| 24704 | n = 20; |
| 24705 | } |
| 24706 | if( x.Y<0 ){ |
| 24707 | zBuf[0] = '-'; |
| 24708 | sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); |
| 24709 | }else{ |
| 24710 | sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); |
| 24711 | } |
| 24712 | } |
| 24713 | } |
| 24714 | |
| 24715 | /* |
| @@ -24644,24 +24722,37 @@ | |
| 24722 | int argc, |
| 24723 | sqlite3_value **argv |
| 24724 | ){ |
| 24725 | DateTime x; |
| 24726 | if( isDate(context, argc, argv, &x)==0 ){ |
| 24727 | int s, n; |
| 24728 | char zBuf[16]; |
| 24729 | computeHMS(&x); |
| 24730 | zBuf[0] = '0' + (x.h/10)%10; |
| 24731 | zBuf[1] = '0' + (x.h)%10; |
| 24732 | zBuf[2] = ':'; |
| 24733 | zBuf[3] = '0' + (x.m/10)%10; |
| 24734 | zBuf[4] = '0' + (x.m)%10; |
| 24735 | zBuf[5] = ':'; |
| 24736 | if( x.useSubsec ){ |
| 24737 | s = (int)1000.0*x.s; |
| 24738 | zBuf[6] = '0' + (s/10000)%10; |
| 24739 | zBuf[7] = '0' + (s/1000)%10; |
| 24740 | zBuf[8] = '.'; |
| 24741 | zBuf[9] = '0' + (s/100)%10; |
| 24742 | zBuf[10] = '0' + (s/10)%10; |
| 24743 | zBuf[11] = '0' + (s)%10; |
| 24744 | zBuf[12] = 0; |
| 24745 | n = 12; |
| 24746 | }else{ |
| 24747 | s = (int)x.s; |
| 24748 | zBuf[6] = '0' + (s/10)%10; |
| 24749 | zBuf[7] = '0' + (s)%10; |
| 24750 | zBuf[8] = 0; |
| 24751 | n = 8; |
| 24752 | } |
| 24753 | sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); |
| 24754 | } |
| 24755 | } |
| 24756 | |
| 24757 | /* |
| 24758 | ** date( TIMESTRING, MOD, MOD, ...) |
| @@ -24788,12 +24879,17 @@ | |
| 24879 | case 'M': { |
| 24880 | sqlite3_str_appendf(&sRes,"%02d",x.m); |
| 24881 | break; |
| 24882 | } |
| 24883 | case 's': { |
| 24884 | if( x.useSubsec ){ |
| 24885 | sqlite3_str_appendf(&sRes,"%.3f", |
| 24886 | (x.iJD - 21086676*(i64)10000000)/1000.0); |
| 24887 | }else{ |
| 24888 | i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); |
| 24889 | sqlite3_str_appendf(&sRes,"%lld",iS); |
| 24890 | } |
| 24891 | break; |
| 24892 | } |
| 24893 | case 'S': { |
| 24894 | sqlite3_str_appendf(&sRes,"%02d",(int)x.s); |
| 24895 | break; |
| @@ -30667,10 +30763,11 @@ | |
| 30763 | nsd = 16 + flag_altform2*10; |
| 30764 | bufpt = buf; |
| 30765 | { |
| 30766 | i64 szBufNeeded; /* Size of a temporary buffer needed */ |
| 30767 | szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; |
| 30768 | if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; |
| 30769 | if( szBufNeeded > etBUFSIZE ){ |
| 30770 | bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); |
| 30771 | if( bufpt==0 ) return; |
| 30772 | } |
| 30773 | } |
| @@ -30684,14 +30781,16 @@ | |
| 30781 | if( e2<0 ){ |
| 30782 | *(bufpt++) = '0'; |
| 30783 | }else if( msd>0 ){ |
| 30784 | for(; e2>=0; e2--){ |
| 30785 | *(bufpt++) = et_getdigit_int(&longvalue,&msd); |
| 30786 | if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; |
| 30787 | } |
| 30788 | }else{ |
| 30789 | for(; e2>=0; e2--){ |
| 30790 | *(bufpt++) = et_getdigit(&realvalue,&nsd); |
| 30791 | if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ','; |
| 30792 | } |
| 30793 | } |
| 30794 | /* The decimal point */ |
| 30795 | if( flag_dp ){ |
| 30796 | *(bufpt++) = '.'; |
| @@ -34434,17 +34533,19 @@ | |
| 34533 | }else{ |
| 34534 | x = v; |
| 34535 | } |
| 34536 | i = sizeof(zTemp)-2; |
| 34537 | zTemp[sizeof(zTemp)-1] = 0; |
| 34538 | while( 1 /*exit-by-break*/ ){ |
| 34539 | zTemp[i] = (x%10) + '0'; |
| 34540 | x = x/10; |
| 34541 | if( x==0 ) break; |
| 34542 | i--; |
| 34543 | }; |
| 34544 | if( v<0 ) zTemp[--i] = '-'; |
| 34545 | memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); |
| 34546 | return sizeof(zTemp)-1-i; |
| 34547 | } |
| 34548 | |
| 34549 | /* |
| 34550 | ** Compare the 19-character string zNum against the text representation |
| 34551 | ** value 2^63: 9223372036854775808. Return negative, zero, or positive |
| @@ -34605,11 +34706,13 @@ | |
| 34706 | for(i=2; z[i]=='0'; i++){} |
| 34707 | for(k=i; sqlite3Isxdigit(z[k]); k++){ |
| 34708 | u = u*16 + sqlite3HexToInt(z[k]); |
| 34709 | } |
| 34710 | memcpy(pOut, &u, 8); |
| 34711 | if( k-i>16 ) return 2; |
| 34712 | if( z[k]!=0 ) return 1; |
| 34713 | return 0; |
| 34714 | }else |
| 34715 | #endif /* SQLITE_OMIT_HEX_INTEGER */ |
| 34716 | { |
| 34717 | return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); |
| 34718 | } |
| @@ -34641,11 +34744,11 @@ | |
| 34744 | && sqlite3Isxdigit(zNum[2]) |
| 34745 | ){ |
| 34746 | u32 u = 0; |
| 34747 | zNum += 2; |
| 34748 | while( zNum[0]=='0' ) zNum++; |
| 34749 | for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ |
| 34750 | u = u*16 + sqlite3HexToInt(zNum[i]); |
| 34751 | } |
| 34752 | if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ |
| 34753 | memcpy(pValue, &u, 4); |
| 34754 | return 1; |
| @@ -37137,11 +37240,11 @@ | |
| 37240 | # define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 37241 | # endif |
| 37242 | #endif |
| 37243 | |
| 37244 | /* Use pread() and pwrite() if they are available */ |
| 37245 | #if defined(__APPLE__) || defined(__linux__) |
| 37246 | # define HAVE_PREAD 1 |
| 37247 | # define HAVE_PWRITE 1 |
| 37248 | #endif |
| 37249 | #if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) |
| 37250 | # undef USE_PREAD |
| @@ -40387,16 +40490,10 @@ | |
| 40490 | |
| 40491 | /* |
| 40492 | ** Seek to the offset passed as the second argument, then read cnt |
| 40493 | ** bytes into pBuf. Return the number of bytes actually read. |
| 40494 | ** |
| 40495 | ** To avoid stomping the errno value on a failed read the lastErrno value |
| 40496 | ** is set before returning. |
| 40497 | */ |
| 40498 | static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ |
| 40499 | int got; |
| @@ -50419,11 +50516,11 @@ | |
| 50516 | &extendedParameters); |
| 50517 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50518 | if( isReadWrite ){ |
| 50519 | int rc2, isRO = 0; |
| 50520 | sqlite3BeginBenignMalloc(); |
| 50521 | rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); |
| 50522 | sqlite3EndBenignMalloc(); |
| 50523 | if( rc2==SQLITE_OK && isRO ) break; |
| 50524 | } |
| 50525 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50526 | #else |
| @@ -50436,11 +50533,11 @@ | |
| 50533 | NULL); |
| 50534 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50535 | if( isReadWrite ){ |
| 50536 | int rc2, isRO = 0; |
| 50537 | sqlite3BeginBenignMalloc(); |
| 50538 | rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); |
| 50539 | sqlite3EndBenignMalloc(); |
| 50540 | if( rc2==SQLITE_OK && isRO ) break; |
| 50541 | } |
| 50542 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50543 | #endif |
| @@ -50456,11 +50553,11 @@ | |
| 50553 | NULL); |
| 50554 | if( h!=INVALID_HANDLE_VALUE ) break; |
| 50555 | if( isReadWrite ){ |
| 50556 | int rc2, isRO = 0; |
| 50557 | sqlite3BeginBenignMalloc(); |
| 50558 | rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO); |
| 50559 | sqlite3EndBenignMalloc(); |
| 50560 | if( rc2==SQLITE_OK && isRO ) break; |
| 50561 | } |
| 50562 | }while( winRetryIoerr(&cnt, &lastErrno) ); |
| 50563 | } |
| @@ -50678,10 +50775,17 @@ | |
| 50775 | UNUSED_PARAMETER(pVfs); |
| 50776 | |
| 50777 | SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
| 50778 | OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", |
| 50779 | zFilename, flags, pResOut)); |
| 50780 | |
| 50781 | if( zFilename==0 ){ |
| 50782 | *pResOut = 0; |
| 50783 | OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
| 50784 | zFilename, pResOut, *pResOut)); |
| 50785 | return SQLITE_OK; |
| 50786 | } |
| 50787 | |
| 50788 | zConverted = winConvertFromUtf8Filename(zFilename); |
| 50789 | if( zConverted==0 ){ |
| 50790 | OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); |
| 50791 | return SQLITE_IOERR_NOMEM_BKPT; |
| @@ -52835,15 +52939,19 @@ | |
| 52939 | # define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;} |
| 52940 | static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){ |
| 52941 | PgHdr *pPg; |
| 52942 | unsigned char *a; |
| 52943 | int j; |
| 52944 | if( pLower==0 ){ |
| 52945 | printf("%3d: NULL\n", i); |
| 52946 | }else{ |
| 52947 | pPg = (PgHdr*)pLower->pExtra; |
| 52948 | printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags); |
| 52949 | a = (unsigned char *)pLower->pBuf; |
| 52950 | for(j=0; j<12; j++) printf("%02x", a[j]); |
| 52951 | printf(" ptr %p\n", pPg); |
| 52952 | } |
| 52953 | } |
| 52954 | static void pcacheDump(PCache *pCache){ |
| 52955 | int N; |
| 52956 | int i; |
| 52957 | sqlite3_pcache_page *pLower; |
| @@ -52852,13 +52960,12 @@ | |
| 52960 | if( pCache->pCache==0 ) return; |
| 52961 | N = sqlite3PcachePagecount(pCache); |
| 52962 | if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump; |
| 52963 | for(i=1; i<=N; i++){ |
| 52964 | pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0); |
| 52965 | pcachePageTrace(i, pLower); |
| 52966 | if( pLower && ((PgHdr*)pLower)->pPage==0 ){ |
| 52967 | sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0); |
| 52968 | } |
| 52969 | } |
| 52970 | } |
| 52971 | #else |
| @@ -58242,10 +58349,12 @@ | |
| 58349 | */ |
| 58350 | static int pager_truncate(Pager *pPager, Pgno nPage){ |
| 58351 | int rc = SQLITE_OK; |
| 58352 | assert( pPager->eState!=PAGER_ERROR ); |
| 58353 | assert( pPager->eState!=PAGER_READER ); |
| 58354 | PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage)); |
| 58355 | |
| 58356 | |
| 58357 | if( isOpen(pPager->fd) |
| 58358 | && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) |
| 58359 | ){ |
| 58360 | i64 currentSize, newSize; |
| @@ -61159,10 +61268,14 @@ | |
| 61268 | |
| 61269 | assert( !isOpen(pPager->fd) || !MEMDB ); |
| 61270 | if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){ |
| 61271 | if( pgno>pPager->mxPgno ){ |
| 61272 | rc = SQLITE_FULL; |
| 61273 | if( pgno<=pPager->dbSize ){ |
| 61274 | sqlite3PcacheRelease(pPg); |
| 61275 | pPg = 0; |
| 61276 | } |
| 61277 | goto pager_acquire_err; |
| 61278 | } |
| 61279 | if( noContent ){ |
| 61280 | /* Failure to set the bits in the InJournal bit-vectors is benign. |
| 61281 | ** It merely means that we might do some extra work to journal a |
| @@ -61323,14 +61436,16 @@ | |
| 61436 | } |
| 61437 | |
| 61438 | /* |
| 61439 | ** Release a page reference. |
| 61440 | ** |
| 61441 | ** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used |
| 61442 | ** if we know that the page being released is not the last reference to page1. |
| 61443 | ** The btree layer always holds page1 open until the end, so these first |
| 61444 | ** two routines can be used to release any page other than BtShared.pPage1. |
| 61445 | ** The assert() at tag-20230419-2 proves that this constraint is always |
| 61446 | ** honored. |
| 61447 | ** |
| 61448 | ** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine |
| 61449 | ** checks the total number of outstanding pages and if the number of |
| 61450 | ** pages reaches zero it drops the database lock. |
| 61451 | */ |
| @@ -61342,11 +61457,11 @@ | |
| 61457 | pagerReleaseMapPage(pPg); |
| 61458 | }else{ |
| 61459 | sqlite3PcacheRelease(pPg); |
| 61460 | } |
| 61461 | /* Do not use this routine to release the last reference to page1 */ |
| 61462 | assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */ |
| 61463 | } |
| 61464 | SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){ |
| 61465 | if( pPg ) sqlite3PagerUnrefNotNull(pPg); |
| 61466 | } |
| 61467 | SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){ |
| @@ -63102,17 +63217,19 @@ | |
| 63217 | ** Attempt to take an exclusive lock on the database file. If a PENDING lock |
| 63218 | ** is obtained instead, immediately release it. |
| 63219 | */ |
| 63220 | static int pagerExclusiveLock(Pager *pPager){ |
| 63221 | int rc; /* Return code */ |
| 63222 | u8 eOrigLock; /* Original lock */ |
| 63223 | |
| 63224 | assert( pPager->eLock>=SHARED_LOCK ); |
| 63225 | eOrigLock = pPager->eLock; |
| 63226 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 63227 | if( rc!=SQLITE_OK ){ |
| 63228 | /* If the attempt to grab the exclusive lock failed, release the |
| 63229 | ** pending lock that may have been obtained instead. */ |
| 63230 | pagerUnlockDb(pPager, eOrigLock); |
| 63231 | } |
| 63232 | |
| 63233 | return rc; |
| 63234 | } |
| 63235 | |
| @@ -64113,23 +64230,44 @@ | |
| 64230 | } |
| 64231 | |
| 64232 | assert( nByte>=8 ); |
| 64233 | assert( (nByte&0x00000007)==0 ); |
| 64234 | assert( nByte<=65536 ); |
| 64235 | assert( nByte%4==0 ); |
| 64236 | |
| 64237 | if( !nativeCksum ){ |
| 64238 | do { |
| 64239 | s1 += BYTESWAP32(aData[0]) + s2; |
| 64240 | s2 += BYTESWAP32(aData[1]) + s1; |
| 64241 | aData += 2; |
| 64242 | }while( aData<aEnd ); |
| 64243 | }else if( nByte%64==0 ){ |
| 64244 | do { |
| 64245 | s1 += *aData++ + s2; |
| 64246 | s2 += *aData++ + s1; |
| 64247 | s1 += *aData++ + s2; |
| 64248 | s2 += *aData++ + s1; |
| 64249 | s1 += *aData++ + s2; |
| 64250 | s2 += *aData++ + s1; |
| 64251 | s1 += *aData++ + s2; |
| 64252 | s2 += *aData++ + s1; |
| 64253 | s1 += *aData++ + s2; |
| 64254 | s2 += *aData++ + s1; |
| 64255 | s1 += *aData++ + s2; |
| 64256 | s2 += *aData++ + s1; |
| 64257 | s1 += *aData++ + s2; |
| 64258 | s2 += *aData++ + s1; |
| 64259 | s1 += *aData++ + s2; |
| 64260 | s2 += *aData++ + s1; |
| 64261 | }while( aData<aEnd ); |
| 64262 | }else{ |
| 64263 | do { |
| 64264 | s1 += *aData++ + s2; |
| 64265 | s2 += *aData++ + s1; |
| 64266 | }while( aData<aEnd ); |
| 64267 | } |
| 64268 | assert( aData==aEnd ); |
| 64269 | |
| 64270 | aOut[0] = s1; |
| 64271 | aOut[1] = s2; |
| 64272 | } |
| 64273 | |
| @@ -67056,11 +67194,13 @@ | |
| 67194 | if( pWal->syncHeader ){ |
| 67195 | rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags)); |
| 67196 | if( rc ) return rc; |
| 67197 | } |
| 67198 | } |
| 67199 | if( (int)pWal->szPage!=szPage ){ |
| 67200 | return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */ |
| 67201 | } |
| 67202 | |
| 67203 | /* Setup information needed to write frames into the WAL */ |
| 67204 | w.pWal = pWal; |
| 67205 | w.pFd = pWal->pWalFd; |
| 67206 | w.iSyncPoint = 0; |
| @@ -67716,11 +67856,11 @@ | |
| 67856 | ** Cell content makes use of variable length integers. A variable |
| 67857 | ** length integer is 1 to 9 bytes where the lower 7 bits of each |
| 67858 | ** byte are used. The integer consists of all bytes that have bit 8 set and |
| 67859 | ** the first byte with bit 8 clear. The most significant byte of the integer |
| 67860 | ** appears first. A variable-length integer may not be more than 9 bytes long. |
| 67861 | ** As a special case, all 8 bits of the 9th byte are used as data. This |
| 67862 | ** allows a 64-bit integer to be encoded in 9 bytes. |
| 67863 | ** |
| 67864 | ** 0x00 becomes 0x00000000 |
| 67865 | ** 0x7f becomes 0x0000007f |
| 67866 | ** 0x81 0x00 becomes 0x00000080 |
| @@ -68100,11 +68240,11 @@ | |
| 68240 | ** Legal values for BtCursor.curFlags |
| 68241 | */ |
| 68242 | #define BTCF_WriteFlag 0x01 /* True if a write cursor */ |
| 68243 | #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ |
| 68244 | #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ |
| 68245 | #define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ |
| 68246 | #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ |
| 68247 | #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ |
| 68248 | #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ |
| 68249 | |
| 68250 | /* |
| @@ -76202,11 +76342,11 @@ | |
| 76342 | aAfter[j] = iAfter; |
| 76343 | break; |
| 76344 | } |
| 76345 | } |
| 76346 | if( j>=nFree ){ |
| 76347 | if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){ |
| 76348 | for(j=0; j<nFree; j++){ |
| 76349 | freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]); |
| 76350 | } |
| 76351 | nFree = 0; |
| 76352 | } |
| @@ -76270,11 +76410,11 @@ | |
| 76410 | int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); |
| 76411 | assert( nCell>=nTail ); |
| 76412 | nCell -= nTail; |
| 76413 | } |
| 76414 | |
| 76415 | pData = &aData[get2byte(&aData[hdr+5])]; |
| 76416 | if( pData<pBegin ) goto editpage_fail; |
| 76417 | if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail; |
| 76418 | |
| 76419 | /* Add cells to the start of the page */ |
| 76420 | if( iNew<iOld ){ |
| @@ -77907,11 +78047,11 @@ | |
| 78047 | return btreeOverwriteCell(pCur, &x2); |
| 78048 | } |
| 78049 | } |
| 78050 | } |
| 78051 | assert( pCur->eState==CURSOR_VALID |
| 78052 | || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); |
| 78053 | |
| 78054 | pPage = pCur->pPage; |
| 78055 | assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) ); |
| 78056 | assert( pPage->leaf || !pPage->intKey ); |
| 78057 | if( pPage->nFree<0 ){ |
| @@ -80157,17 +80297,11 @@ | |
| 80297 | assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); |
| 80298 | assert( p->bDestLocked ); |
| 80299 | assert( !isFatalError(p->rc) ); |
| 80300 | assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); |
| 80301 | assert( zSrcData ); |
| 80302 | assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); |
| 80303 | |
| 80304 | /* This loop runs once for each destination page spanned by the source |
| 80305 | ** page. For each iteration, variable iOff is set to the byte offset |
| 80306 | ** of the destination page. |
| 80307 | */ |
| @@ -80296,11 +80430,14 @@ | |
| 80430 | /* Do not allow backup if the destination database is in WAL mode |
| 80431 | ** and the page sizes are different between source and destination */ |
| 80432 | pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); |
| 80433 | pgszDest = sqlite3BtreeGetPageSize(p->pDest); |
| 80434 | destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); |
| 80435 | if( SQLITE_OK==rc |
| 80436 | && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) |
| 80437 | && pgszSrc!=pgszDest |
| 80438 | ){ |
| 80439 | rc = SQLITE_READONLY; |
| 80440 | } |
| 80441 | |
| 80442 | /* Now that there is a read-lock on the source database, query the |
| 80443 | ** source pager for the number of pages in the database. |
| @@ -80845,10 +80982,11 @@ | |
| 80982 | Mem tmp; |
| 80983 | char zBuf[100]; |
| 80984 | char *z; |
| 80985 | int i, j, incr; |
| 80986 | if( (p->flags & MEM_Str)==0 ) return 1; |
| 80987 | if( p->db && p->db->mallocFailed ) return 1; |
| 80988 | if( p->flags & MEM_Term ){ |
| 80989 | /* Insure that the string is properly zero-terminated. Pay particular |
| 80990 | ** attention to the case where p->n is odd */ |
| 80991 | if( p->szMalloc>0 && p->z==p->zMalloc ){ |
| 80992 | assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 ); |
| @@ -83508,10 +83646,12 @@ | |
| 83646 | static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ |
| 83647 | int nMaxArgs = *pMaxFuncArgs; |
| 83648 | Op *pOp; |
| 83649 | Parse *pParse = p->pParse; |
| 83650 | int *aLabel = pParse->aLabel; |
| 83651 | |
| 83652 | assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */ |
| 83653 | p->readOnly = 1; |
| 83654 | p->bIsReader = 0; |
| 83655 | pOp = &p->aOp[p->nOp-1]; |
| 83656 | assert( p->aOp[0].opcode==OP_Init ); |
| 83657 | while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){ |
| @@ -83567,10 +83707,11 @@ | |
| 83707 | /* The mkopcodeh.tcl script has so arranged things that the only |
| 83708 | ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to |
| 83709 | ** have non-negative values for P2. */ |
| 83710 | assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); |
| 83711 | assert( ADDR(pOp->p2)<-pParse->nLabel ); |
| 83712 | assert( aLabel!=0 ); /* True because of tag-20230419-1 */ |
| 83713 | pOp->p2 = aLabel[ADDR(pOp->p2)]; |
| 83714 | } |
| 83715 | break; |
| 83716 | } |
| 83717 | } |
| @@ -84310,11 +84451,11 @@ | |
| 84451 | } |
| 84452 | } |
| 84453 | |
| 84454 | /* Return the most recently added opcode |
| 84455 | */ |
| 84456 | SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){ |
| 84457 | return sqlite3VdbeGetOp(p, p->nOp - 1); |
| 84458 | } |
| 84459 | |
| 84460 | #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) |
| 84461 | /* |
| @@ -88039,10 +88180,20 @@ | |
| 88180 | sqlite3 *db = v->db; |
| 88181 | i64 iKey2; |
| 88182 | PreUpdate preupdate; |
| 88183 | const char *zTbl = pTab->zName; |
| 88184 | static const u8 fakeSortOrder = 0; |
| 88185 | #ifdef SQLITE_DEBUG |
| 88186 | int nRealCol; |
| 88187 | if( pTab->tabFlags & TF_WithoutRowid ){ |
| 88188 | nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn; |
| 88189 | }else if( pTab->tabFlags & TF_HasVirtual ){ |
| 88190 | nRealCol = pTab->nNVCol; |
| 88191 | }else{ |
| 88192 | nRealCol = pTab->nCol; |
| 88193 | } |
| 88194 | #endif |
| 88195 | |
| 88196 | assert( db->pPreUpdate==0 ); |
| 88197 | memset(&preupdate, 0, sizeof(PreUpdate)); |
| 88198 | if( HasRowid(pTab)==0 ){ |
| 88199 | iKey1 = iKey2 = 0; |
| @@ -88055,12 +88206,12 @@ | |
| 88206 | } |
| 88207 | } |
| 88208 | |
| 88209 | assert( pCsr!=0 ); |
| 88210 | assert( pCsr->eCurType==CURTYPE_BTREE ); |
| 88211 | assert( pCsr->nField==nRealCol |
| 88212 | || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1) |
| 88213 | ); |
| 88214 | |
| 88215 | preupdate.v = v; |
| 88216 | preupdate.pCsr = pCsr; |
| 88217 | preupdate.op = op; |
| @@ -89429,13 +89580,13 @@ | |
| 89580 | p = (Vdbe *)pStmt; |
| 89581 | db = p->db; |
| 89582 | assert( db!=0 ); |
| 89583 | n = sqlite3_column_count(pStmt); |
| 89584 | if( N<n && N>=0 ){ |
| 89585 | u8 prior_mallocFailed = db->mallocFailed; |
| 89586 | N += useType*n; |
| 89587 | sqlite3_mutex_enter(db->mutex); |
| 89588 | #ifndef SQLITE_OMIT_UTF16 |
| 89589 | if( useUtf16 ){ |
| 89590 | ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); |
| 89591 | }else |
| 89592 | #endif |
| @@ -89443,11 +89594,12 @@ | |
| 89594 | ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); |
| 89595 | } |
| 89596 | /* A malloc may have failed inside of the _text() call. If this |
| 89597 | ** is the case, clear the mallocFailed flag and return NULL. |
| 89598 | */ |
| 89599 | assert( db->mallocFailed==0 || db->mallocFailed==1 ); |
| 89600 | if( db->mallocFailed > prior_mallocFailed ){ |
| 89601 | sqlite3OomClear(db); |
| 89602 | ret = 0; |
| 89603 | } |
| 89604 | sqlite3_mutex_leave(db->mutex); |
| 89605 | } |
| @@ -92987,11 +93139,11 @@ | |
| 93139 | } |
| 93140 | |
| 93141 | /* Opcode: Jump P1 P2 P3 * * |
| 93142 | ** |
| 93143 | ** Jump to the instruction at address P1, P2, or P3 depending on whether |
| 93144 | ** in the most recent OP_Compare instruction the P1 vector was less than, |
| 93145 | ** equal to, or greater than the P2 vector, respectively. |
| 93146 | ** |
| 93147 | ** This opcode must immediately follow an OP_Compare opcode. |
| 93148 | */ |
| 93149 | case OP_Jump: { /* jump */ |
| @@ -93333,11 +93485,11 @@ | |
| 93485 | */ |
| 93486 | case OP_IfNullRow: { /* jump */ |
| 93487 | VdbeCursor *pC; |
| 93488 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 93489 | pC = p->apCsr[pOp->p1]; |
| 93490 | if( pC && pC->nullRow ){ |
| 93491 | sqlite3VdbeMemSetNull(aMem + pOp->p3); |
| 93492 | goto jump_to_p2; |
| 93493 | } |
| 93494 | break; |
| 93495 | } |
| @@ -93828,11 +93980,11 @@ | |
| 93980 | pIn1->flags |= MEM_IntReal; |
| 93981 | pIn1->flags &= ~MEM_Int; |
| 93982 | }else{ |
| 93983 | pIn1->u.r = (double)pIn1->u.i; |
| 93984 | pIn1->flags |= MEM_Real; |
| 93985 | pIn1->flags &= ~(MEM_Int|MEM_Str); |
| 93986 | } |
| 93987 | } |
| 93988 | REGISTER_TRACE((int)(pIn1-aMem), pIn1); |
| 93989 | zAffinity++; |
| 93990 | if( zAffinity[0]==0 ) break; |
| @@ -99841,11 +99993,11 @@ | |
| 99993 | *ppBlob = (sqlite3_blob *)pBlob; |
| 99994 | }else{ |
| 99995 | if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); |
| 99996 | sqlite3DbFree(db, pBlob); |
| 99997 | } |
| 99998 | sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); |
| 99999 | sqlite3DbFree(db, zErr); |
| 100000 | sqlite3ParseObjectReset(&sParse); |
| 100001 | rc = sqlite3ApiExit(db, rc); |
| 100002 | sqlite3_mutex_leave(db->mutex); |
| 100003 | return rc; |
| @@ -100000,11 +100152,11 @@ | |
| 100152 | }else{ |
| 100153 | char *zErr; |
| 100154 | ((Vdbe*)p->pStmt)->rc = SQLITE_OK; |
| 100155 | rc = blobSeekToRow(p, iRow, &zErr); |
| 100156 | if( rc!=SQLITE_OK ){ |
| 100157 | sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr); |
| 100158 | sqlite3DbFree(db, zErr); |
| 100159 | } |
| 100160 | assert( rc!=SQLITE_SCHEMA ); |
| 100161 | } |
| 100162 | |
| @@ -107198,28 +107350,31 @@ | |
| 107350 | |
| 107351 | /* |
| 107352 | ** Join two expressions using an AND operator. If either expression is |
| 107353 | ** NULL, then just return the other expression. |
| 107354 | ** |
| 107355 | ** If one side or the other of the AND is known to be false, and neither side |
| 107356 | ** is part of an ON clause, then instead of returning an AND expression, |
| 107357 | ** just return a constant expression with a value of false. |
| 107358 | */ |
| 107359 | SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ |
| 107360 | sqlite3 *db = pParse->db; |
| 107361 | if( pLeft==0 ){ |
| 107362 | return pRight; |
| 107363 | }else if( pRight==0 ){ |
| 107364 | return pLeft; |
| 107365 | }else{ |
| 107366 | u32 f = pLeft->flags | pRight->flags; |
| 107367 | if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse))==EP_IsFalse |
| 107368 | && !IN_RENAME_OBJECT |
| 107369 | ){ |
| 107370 | sqlite3ExprDeferredDelete(pParse, pLeft); |
| 107371 | sqlite3ExprDeferredDelete(pParse, pRight); |
| 107372 | return sqlite3Expr(db, TK_INTEGER, "0"); |
| 107373 | }else{ |
| 107374 | return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); |
| 107375 | } |
| 107376 | } |
| 107377 | } |
| 107378 | |
| 107379 | /* |
| 107380 | ** Construct a new expression node for a function with multiple |
| @@ -108460,16 +108615,21 @@ | |
| 108615 | SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ |
| 108616 | return exprIsConst(p, 3, iCur); |
| 108617 | } |
| 108618 | |
| 108619 | /* |
| 108620 | ** Check pExpr to see if it is an constraint on the single data source |
| 108621 | ** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr |
| 108622 | ** constrains pSrc but does not depend on any other tables or data |
| 108623 | ** sources anywhere else in the query. Return true (non-zero) if pExpr |
| 108624 | ** is a constraint on pSrc only. |
| 108625 | ** |
| 108626 | ** This is an optimization. False negatives will perhaps cause slower |
| 108627 | ** queries, but false positives will yield incorrect answers. So when in |
| 108628 | ** doubt, return 0. |
| 108629 | ** |
| 108630 | ** To be an single-source constraint, the following must be true: |
| 108631 | ** |
| 108632 | ** (1) pExpr cannot refer to any table other than pSrc->iCursor. |
| 108633 | ** |
| 108634 | ** (2) pExpr cannot use subqueries or non-deterministic functions. |
| 108635 | ** |
| @@ -108476,25 +108636,56 @@ | |
| 108636 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. |
| 108637 | ** (Is there some way to relax this constraint?) |
| 108638 | ** |
| 108639 | ** (4) If pSrc is the right operand of a LEFT JOIN, then... |
| 108640 | ** (4a) pExpr must come from an ON clause.. |
| 108641 | ** (4b) and specifically the ON clause associated with the LEFT JOIN. |
| 108642 | ** |
| 108643 | ** (5) If pSrc is not the right operand of a LEFT JOIN or the left |
| 108644 | ** operand of a RIGHT JOIN, then pExpr must be from the WHERE |
| 108645 | ** clause, not an ON clause. |
| 108646 | ** |
| 108647 | ** (6) Either: |
| 108648 | ** |
| 108649 | ** (6a) pExpr does not originate in an ON or USING clause, or |
| 108650 | ** |
| 108651 | ** (6b) The ON or USING clause from which pExpr is derived is |
| 108652 | ** not to the left of a RIGHT JOIN (or FULL JOIN). |
| 108653 | ** |
| 108654 | ** Without this restriction, accepting pExpr as a single-table |
| 108655 | ** constraint might move the the ON/USING filter expression |
| 108656 | ** from the left side of a RIGHT JOIN over to the right side, |
| 108657 | ** which leads to incorrect answers. See also restriction (9) |
| 108658 | ** on push-down. |
| 108659 | */ |
| 108660 | SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( |
| 108661 | Expr *pExpr, /* The constraint */ |
| 108662 | const SrcList *pSrcList, /* Complete FROM clause */ |
| 108663 | int iSrc /* Which element of pSrcList to use */ |
| 108664 | ){ |
| 108665 | const SrcItem *pSrc = &pSrcList->a[iSrc]; |
| 108666 | if( pSrc->fg.jointype & JT_LTORJ ){ |
| 108667 | return 0; /* rule (3) */ |
| 108668 | } |
| 108669 | if( pSrc->fg.jointype & JT_LEFT ){ |
| 108670 | if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ |
| 108671 | if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ |
| 108672 | }else{ |
| 108673 | if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ |
| 108674 | } |
| 108675 | if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */ |
| 108676 | && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */ |
| 108677 | ){ |
| 108678 | int jj; |
| 108679 | for(jj=0; jj<iSrc; jj++){ |
| 108680 | if( pExpr->w.iJoin==pSrcList->a[jj].iCursor ){ |
| 108681 | if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){ |
| 108682 | return 0; /* restriction (6) */ |
| 108683 | } |
| 108684 | break; |
| 108685 | } |
| 108686 | } |
| 108687 | } |
| 108688 | return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ |
| 108689 | } |
| 108690 | |
| 108691 | |
| @@ -112373,11 +112564,11 @@ | |
| 112564 | int iAgg = pExpr->iAgg; |
| 112565 | Parse *pParse = pWalker->pParse; |
| 112566 | sqlite3 *db = pParse->db; |
| 112567 | assert( iAgg>=0 ); |
| 112568 | if( pExpr->op!=TK_AGG_FUNCTION ){ |
| 112569 | if( iAgg<pAggInfo->nColumn |
| 112570 | && pAggInfo->aCol[iAgg].pCExpr==pExpr |
| 112571 | ){ |
| 112572 | pExpr = sqlite3ExprDup(db, pExpr, 0); |
| 112573 | if( pExpr ){ |
| 112574 | pAggInfo->aCol[iAgg].pCExpr = pExpr; |
| @@ -112578,11 +112769,11 @@ | |
| 112769 | findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr); |
| 112770 | break; |
| 112771 | } /* endif pExpr->iTable==pItem->iCursor */ |
| 112772 | } /* end loop over pSrcList */ |
| 112773 | } |
| 112774 | return WRC_Continue; |
| 112775 | } |
| 112776 | case TK_AGG_FUNCTION: { |
| 112777 | if( (pNC->ncFlags & NC_InAggFunc)==0 |
| 112778 | && pWalker->walkerDepth==pExpr->op2 |
| 112779 | ){ |
| @@ -114075,10 +114266,23 @@ | |
| 114266 | } |
| 114267 | |
| 114268 | sqlite3_free(zQuot); |
| 114269 | return rc; |
| 114270 | } |
| 114271 | |
| 114272 | /* |
| 114273 | ** Set all pEList->a[].fg.eEName fields in the expression-list to val. |
| 114274 | */ |
| 114275 | static void renameSetENames(ExprList *pEList, int val){ |
| 114276 | if( pEList ){ |
| 114277 | int i; |
| 114278 | for(i=0; i<pEList->nExpr; i++){ |
| 114279 | assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); |
| 114280 | pEList->a[i].fg.eEName = val; |
| 114281 | } |
| 114282 | } |
| 114283 | } |
| 114284 | |
| 114285 | /* |
| 114286 | ** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming |
| 114287 | ** it was read from the schema of database zDb. Return SQLITE_OK if |
| 114288 | ** successful. Otherwise, return an SQLite error code and leave an error |
| @@ -114123,11 +114327,21 @@ | |
| 114327 | if( pSel==0 ){ |
| 114328 | pStep->pExprList = 0; |
| 114329 | pSrc = 0; |
| 114330 | rc = SQLITE_NOMEM; |
| 114331 | }else{ |
| 114332 | /* pStep->pExprList contains an expression-list used for an UPDATE |
| 114333 | ** statement. So the a[].zEName values are the RHS of the |
| 114334 | ** "<col> = <expr>" clauses of the UPDATE statement. So, before |
| 114335 | ** running SelectPrep(), change all the eEName values in |
| 114336 | ** pStep->pExprList to ENAME_SPAN (from their current value of |
| 114337 | ** ENAME_NAME). This is to prevent any ids in ON() clauses that are |
| 114338 | ** part of pSrc from being incorrectly resolved against the |
| 114339 | ** a[].zEName values as if they were column aliases. */ |
| 114340 | renameSetENames(pStep->pExprList, ENAME_SPAN); |
| 114341 | sqlite3SelectPrep(pParse, pSel, 0); |
| 114342 | renameSetENames(pStep->pExprList, ENAME_NAME); |
| 114343 | rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; |
| 114344 | assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); |
| 114345 | assert( pSrc==pSel->pSrc ); |
| 114346 | if( pStep->pExprList ) pSel->pEList = 0; |
| 114347 | pSel->pSrc = 0; |
| @@ -116859,17 +117073,22 @@ | |
| 117073 | if( zIndex==0 ) continue; |
| 117074 | nSample = sqlite3_column_int(pStmt, 1); |
| 117075 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 117076 | assert( pIdx==0 || pIdx->nSample==0 ); |
| 117077 | if( pIdx==0 ) continue; |
| 117078 | if( pIdx->aSample!=0 ){ |
| 117079 | /* The same index appears in sqlite_stat4 under multiple names */ |
| 117080 | continue; |
| 117081 | } |
| 117082 | assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 ); |
| 117083 | if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){ |
| 117084 | nIdxCol = pIdx->nKeyCol; |
| 117085 | }else{ |
| 117086 | nIdxCol = pIdx->nColumn; |
| 117087 | } |
| 117088 | pIdx->nSampleCol = nIdxCol; |
| 117089 | pIdx->mxSample = nSample; |
| 117090 | nByte = sizeof(IndexSample) * nSample; |
| 117091 | nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; |
| 117092 | nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ |
| 117093 | |
| 117094 | pIdx->aSample = sqlite3DbMallocZero(db, nByte); |
| @@ -116905,10 +117124,15 @@ | |
| 117124 | |
| 117125 | zIndex = (char *)sqlite3_column_text(pStmt, 0); |
| 117126 | if( zIndex==0 ) continue; |
| 117127 | pIdx = findIndexOrPrimaryKey(db, zIndex, zDb); |
| 117128 | if( pIdx==0 ) continue; |
| 117129 | if( pIdx->nSample>=pIdx->mxSample ){ |
| 117130 | /* Too many slots used because the same index appears in |
| 117131 | ** sqlite_stat4 using multiple names */ |
| 117132 | continue; |
| 117133 | } |
| 117134 | /* This next condition is true if data has already been loaded from |
| 117135 | ** the sqlite_stat4 table. */ |
| 117136 | nCol = pIdx->nSampleCol; |
| 117137 | if( pIdx!=pPrevIdx ){ |
| 117138 | initAvgEq(pPrevIdx); |
| @@ -116948,15 +117172,16 @@ | |
| 117172 | static int loadStat4(sqlite3 *db, const char *zDb){ |
| 117173 | int rc = SQLITE_OK; /* Result codes from subroutines */ |
| 117174 | const Table *pStat4; |
| 117175 | |
| 117176 | assert( db->lookaside.bDisable ); |
| 117177 | if( OptimizationEnabled(db, SQLITE_Stat4) |
| 117178 | && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0 |
| 117179 | && IsOrdinaryTable(pStat4) |
| 117180 | ){ |
| 117181 | rc = loadStatTbl(db, |
| 117182 | "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase", |
| 117183 | "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", |
| 117184 | zDb |
| 117185 | ); |
| 117186 | } |
| 117187 | return rc; |
| @@ -118796,11 +119021,11 @@ | |
| 119021 | } |
| 119022 | |
| 119023 | if( IsOrdinaryTable(pTable) ){ |
| 119024 | sqlite3FkDelete(db, pTable); |
| 119025 | } |
| 119026 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 119027 | else if( IsVirtual(pTable) ){ |
| 119028 | sqlite3VtabClear(db, pTable); |
| 119029 | } |
| 119030 | #endif |
| 119031 | else{ |
| @@ -123828,10 +124053,11 @@ | |
| 124053 | db->enc = enc; |
| 124054 | /* EVIDENCE-OF: R-08308-17224 The default collating function for all |
| 124055 | ** strings is BINARY. |
| 124056 | */ |
| 124057 | db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0); |
| 124058 | sqlite3ExpirePreparedStatements(db, 1); |
| 124059 | } |
| 124060 | |
| 124061 | /* |
| 124062 | ** This function is responsible for invoking the collation factory callback |
| 124063 | ** or substituting a collation sequence of a different encoding when the |
| @@ -126724,11 +126950,11 @@ | |
| 126950 | |
| 126951 | #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION |
| 126952 | /* |
| 126953 | ** The "unknown" function is automatically substituted in place of |
| 126954 | ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN |
| 126955 | ** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. |
| 126956 | ** When the "sqlite3" command-line shell is built using this functionality, |
| 126957 | ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries |
| 126958 | ** involving application-defined functions to be examined in a generic |
| 126959 | ** sqlite3 shell. |
| 126960 | */ |
| @@ -129027,26 +129253,26 @@ | |
| 129253 | zFrom = pFKey->pFrom->zName; |
| 129254 | nFrom = sqlite3Strlen30(zFrom); |
| 129255 | |
| 129256 | if( action==OE_Restrict ){ |
| 129257 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 129258 | SrcList *pSrc; |
| 129259 | Expr *pRaise; |
| 129260 | |
| 129261 | pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed"); |
| 129262 | if( pRaise ){ |
| 129263 | pRaise->affExpr = OE_Abort; |
| 129264 | } |
| 129265 | pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); |
| 129266 | if( pSrc ){ |
| 129267 | assert( pSrc->nSrc==1 ); |
| 129268 | pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom); |
| 129269 | pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); |
| 129270 | } |
| 129271 | pSelect = sqlite3SelectNew(pParse, |
| 129272 | sqlite3ExprListAppend(pParse, 0, pRaise), |
| 129273 | pSrc, |
| 129274 | pWhere, |
| 129275 | 0, 0, 0, 0, 0 |
| 129276 | ); |
| 129277 | pWhere = 0; |
| 129278 | } |
| @@ -137995,11 +138221,13 @@ | |
| 138221 | encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; |
| 138222 | if( encoding==0 ) encoding = SQLITE_UTF8; |
| 138223 | #else |
| 138224 | encoding = SQLITE_UTF8; |
| 138225 | #endif |
| 138226 | if( db->nVdbeActive>0 && encoding!=ENC(db) |
| 138227 | && (db->mDbFlags & DBFLAG_Vacuum)==0 |
| 138228 | ){ |
| 138229 | rc = SQLITE_LOCKED; |
| 138230 | goto initone_error_out; |
| 138231 | }else{ |
| 138232 | sqlite3SetTextEncoding(db, encoding); |
| 138233 | } |
| @@ -138389,11 +138617,15 @@ | |
| 138617 | sParse.pOuterParse = db->pParse; |
| 138618 | db->pParse = &sParse; |
| 138619 | sParse.db = db; |
| 138620 | sParse.pReprepare = pReprepare; |
| 138621 | assert( ppStmt && *ppStmt==0 ); |
| 138622 | if( db->mallocFailed ){ |
| 138623 | sqlite3ErrorMsg(&sParse, "out of memory"); |
| 138624 | db->errCode = rc = SQLITE_NOMEM; |
| 138625 | goto end_prepare; |
| 138626 | } |
| 138627 | assert( sqlite3_mutex_held(db->mutex) ); |
| 138628 | |
| 138629 | /* For a long-term use prepared statement avoid the use of |
| 138630 | ** lookaside memory. |
| 138631 | */ |
| @@ -139478,11 +139710,11 @@ | |
| 139710 | ** by a prior OP_MakeRecord. In this case nData==1 and regData |
| 139711 | ** will be completely unrelated to regOrigData. |
| 139712 | ** (2) All output columns are included in the sort record. In that |
| 139713 | ** case regData==regOrigData. |
| 139714 | ** (3) Some output columns are omitted from the sort record due to |
| 139715 | ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the |
| 139716 | ** SQLITE_ECEL_OMITREF optimization, or due to the |
| 139717 | ** SortCtx.pDeferredRowLoad optimiation. In any of these cases |
| 139718 | ** regOrigData is 0 to prevent this routine from trying to copy |
| 139719 | ** values that might not yet exist. |
| 139720 | */ |
| @@ -141079,11 +141311,11 @@ | |
| 141311 | |
| 141312 | assert( pSelect!=0 ); |
| 141313 | assert( (pSelect->selFlags & SF_Resolved)!=0 ); |
| 141314 | assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); |
| 141315 | assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); |
| 141316 | if( db->mallocFailed || IN_RENAME_OBJECT ) return; |
| 141317 | while( pSelect->pPrior ) pSelect = pSelect->pPrior; |
| 141318 | a = pSelect->pEList->a; |
| 141319 | memset(&sNC, 0, sizeof(sNC)); |
| 141320 | sNC.pSrcList = pSelect->pSrc; |
| 141321 | for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ |
| @@ -141124,22 +141356,20 @@ | |
| 141356 | if( sqlite3StdTypeAffinity[j]==pCol->affinity ){ |
| 141357 | zType = sqlite3StdType[j]; |
| 141358 | break; |
| 141359 | } |
| 141360 | } |
| 141361 | } |
| 141362 | } |
| 141363 | if( zType ){ |
| 141364 | i64 m = sqlite3Strlen30(zType); |
| 141365 | n = sqlite3Strlen30(pCol->zCnName); |
| 141366 | pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); |
| 141367 | pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); |
| 141368 | if( pCol->zCnName ){ |
| 141369 | memcpy(&pCol->zCnName[n+1], zType, m+1); |
| 141370 | pCol->colFlags |= COLFLAG_HASTYPE; |
| 141371 | } |
| 141372 | } |
| 141373 | pColl = sqlite3ExprCollSeq(pParse, p); |
| 141374 | if( pColl ){ |
| 141375 | assert( pTab->pIndex==0 ); |
| @@ -143870,25 +144100,50 @@ | |
| 144100 | ** |
| 144101 | ** (8) If the subquery is a compound that uses UNION, INTERSECT, |
| 144102 | ** or EXCEPT, then all of the result set columns for all arms of |
| 144103 | ** the compound must use the BINARY collating sequence. |
| 144104 | ** |
| 144105 | ** (9) All three of the following are true: |
| 144106 | ** |
| 144107 | ** (9a) The WHERE clause expression originates in the ON or USING clause |
| 144108 | ** of a join (either an INNER or an OUTER join), and |
| 144109 | ** |
| 144110 | ** (9b) The subquery is to the right of the ON/USING clause |
| 144111 | ** |
| 144112 | ** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING |
| 144113 | ** clause and the subquery. |
| 144114 | ** |
| 144115 | ** Without this restriction, the push-down optimization might move |
| 144116 | ** the ON/USING filter expression from the left side of a RIGHT JOIN |
| 144117 | ** over to the right side, which leads to incorrect answers. See |
| 144118 | ** also restriction (6) in sqlite3ExprIsSingleTableConstraint(). |
| 144119 | ** |
| 144120 | ** (10) The inner query is not the right-hand table of a RIGHT JOIN. |
| 144121 | ** |
| 144122 | ** (11) The subquery is not a VALUES clause |
| 144123 | ** |
| 144124 | ** Return 0 if no changes are made and non-zero if one or more WHERE clause |
| 144125 | ** terms are duplicated into the subquery. |
| 144126 | */ |
| 144127 | static int pushDownWhereTerms( |
| 144128 | Parse *pParse, /* Parse context (for malloc() and error reporting) */ |
| 144129 | Select *pSubq, /* The subquery whose WHERE clause is to be augmented */ |
| 144130 | Expr *pWhere, /* The WHERE clause of the outer query */ |
| 144131 | SrcList *pSrcList, /* The complete from clause of the outer query */ |
| 144132 | int iSrc /* Which FROM clause term to try to push into */ |
| 144133 | ){ |
| 144134 | Expr *pNew; |
| 144135 | SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */ |
| 144136 | int nChng = 0; |
| 144137 | pSrc = &pSrcList->a[iSrc]; |
| 144138 | if( pWhere==0 ) return 0; |
| 144139 | if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){ |
| 144140 | return 0; /* restrictions (2) and (11) */ |
| 144141 | } |
| 144142 | if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){ |
| 144143 | return 0; /* restrictions (10) */ |
| 144144 | } |
| 144145 | |
| 144146 | if( pSubq->pPrior ){ |
| 144147 | Select *pSel; |
| 144148 | int notUnionAll = 0; |
| 144149 | for(pSel=pSubq; pSel; pSel=pSel->pPrior){ |
| @@ -143939,15 +144194,32 @@ | |
| 144194 | |
| 144195 | if( pSubq->pLimit!=0 ){ |
| 144196 | return 0; /* restriction (3) */ |
| 144197 | } |
| 144198 | while( pWhere->op==TK_AND ){ |
| 144199 | nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc); |
| 144200 | pWhere = pWhere->pLeft; |
| 144201 | } |
| 144202 | |
| 144203 | #if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */ |
| 144204 | if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */ |
| 144205 | && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */ |
| 144206 | ){ |
| 144207 | int jj; |
| 144208 | for(jj=0; jj<iSrc; jj++){ |
| 144209 | if( pWhere->w.iJoin==pSrcList->a[jj].iCursor ){ |
| 144210 | /* If we reach this point, both (9a) and (9b) are satisfied. |
| 144211 | ** The following loop checks (9c): |
| 144212 | */ |
| 144213 | for(jj++; jj<iSrc; jj++){ |
| 144214 | if( (pSrcList->a[jj].fg.jointype & JT_RIGHT)!=0 ){ |
| 144215 | return 0; /* restriction (9) */ |
| 144216 | } |
| 144217 | } |
| 144218 | } |
| 144219 | } |
| 144220 | } |
| 144221 | if( isLeftJoin |
| 144222 | && (ExprHasProperty(pWhere,EP_OuterON)==0 |
| 144223 | || pWhere->w.iJoin!=iCursor) |
| 144224 | ){ |
| 144225 | return 0; /* restriction (4) */ |
| @@ -143957,11 +144229,11 @@ | |
| 144229 | ){ |
| 144230 | return 0; /* restriction (5) */ |
| 144231 | } |
| 144232 | #endif |
| 144233 | |
| 144234 | if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ |
| 144235 | nChng++; |
| 144236 | pSubq->selFlags |= SF_PushDown; |
| 144237 | while( pSubq ){ |
| 144238 | SubstContext x; |
| 144239 | pNew = sqlite3ExprDup(pParse->db, pWhere, 0); |
| @@ -144027,15 +144299,17 @@ | |
| 144299 | if( pX->pPrior && pX->op!=TK_ALL ){ |
| 144300 | /* This optimization does not work for compound subqueries that |
| 144301 | ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */ |
| 144302 | return 0; |
| 144303 | } |
| 144304 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 144305 | if( pX->pWin ){ |
| 144306 | /* This optimization does not work for subqueries that use window |
| 144307 | ** functions. */ |
| 144308 | return 0; |
| 144309 | } |
| 144310 | #endif |
| 144311 | } |
| 144312 | colUsed = pItem->colUsed; |
| 144313 | if( pSub->pOrderBy ){ |
| 144314 | ExprList *pList = pSub->pOrderBy; |
| 144315 | for(j=0; j<pList->nExpr; j++){ |
| @@ -145207,16 +145481,17 @@ | |
| 145481 | assert( pAggInfo->iFirstReg==0 ); |
| 145482 | assert( pSelect!=0 ); |
| 145483 | assert( pSelect->pGroupBy!=0 ); |
| 145484 | pAggInfo->nColumn = pAggInfo->nAccumulator; |
| 145485 | if( ALWAYS(pAggInfo->nSortingColumn>0) ){ |
| 145486 | int mx = pSelect->pGroupBy->nExpr - 1; |
| 145487 | int j, k; |
| 145488 | for(j=0; j<pAggInfo->nColumn; j++){ |
| 145489 | k = pAggInfo->aCol[j].iSorterColumn; |
| 145490 | if( k>mx ) mx = k; |
| 145491 | } |
| 145492 | pAggInfo->nSortingColumn = mx+1; |
| 145493 | } |
| 145494 | analyzeAggFuncArgs(pAggInfo, pNC); |
| 145495 | #if TREETRACE_ENABLED |
| 145496 | if( sqlite3TreeTrace & 0x20 ){ |
| 145497 | IndexedExpr *pIEpr; |
| @@ -145952,11 +146227,11 @@ | |
| 146227 | assert( pItem->iCursor>=0 ); |
| 146228 | unsetJoinExpr(p->pWhere, pItem->iCursor, |
| 146229 | pTabList->a[0].fg.jointype & JT_LTORJ); |
| 146230 | } |
| 146231 | |
| 146232 | /* No futher action if this term of the FROM clause is not a subquery */ |
| 146233 | if( pSub==0 ) continue; |
| 146234 | |
| 146235 | /* Catch mismatch in the declared columns of a view and the number of |
| 146236 | ** columns in the SELECT on the RHS */ |
| 146237 | if( pTab->nCol!=pSub->pEList->nExpr ){ |
| @@ -146149,11 +146424,11 @@ | |
| 146424 | ** inside the subquery. This can help the subquery to run more efficiently. |
| 146425 | */ |
| 146426 | if( OptimizationEnabled(db, SQLITE_PushDown) |
| 146427 | && (pItem->fg.isCte==0 |
| 146428 | || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2)) |
| 146429 | && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i) |
| 146430 | ){ |
| 146431 | #if TREETRACE_ENABLED |
| 146432 | if( sqlite3TreeTrace & 0x4000 ){ |
| 146433 | TREETRACE(0x4000,pParse,p, |
| 146434 | ("After WHERE-clause push-down into subquery %d:\n", pSub->selId)); |
| @@ -151944,11 +152219,14 @@ | |
| 152219 | default: |
| 152220 | xMethod = pMod->xRelease; |
| 152221 | break; |
| 152222 | } |
| 152223 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 152224 | u64 savedFlags = (db->flags & SQLITE_Defensive); |
| 152225 | db->flags &= ~(u64)SQLITE_Defensive; |
| 152226 | rc = xMethod(pVTab->pVtab, iSavepoint); |
| 152227 | db->flags |= savedFlags; |
| 152228 | } |
| 152229 | sqlite3VtabUnlock(pVTab); |
| 152230 | } |
| 152231 | } |
| 152232 | } |
| @@ -153869,10 +154147,13 @@ | |
| 154147 | rc = WRC_Prune; |
| 154148 | reg = ++pWalker->pParse->nMem; /* Register for column value */ |
| 154149 | reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg); |
| 154150 | pExpr->op = TK_REGISTER; |
| 154151 | pExpr->iTable = reg; |
| 154152 | }else if( pExpr->op==TK_TRUEFALSE ){ |
| 154153 | /* Do not walk disabled expressions. tag-20230504-1 */ |
| 154154 | return WRC_Prune; |
| 154155 | } |
| 154156 | return rc; |
| 154157 | } |
| 154158 | |
| 154159 | /* |
| @@ -153970,11 +154251,11 @@ | |
| 154251 | /* If we survive all prior tests, that means this term is worth hinting */ |
| 154252 | pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0)); |
| 154253 | } |
| 154254 | if( pExpr!=0 ){ |
| 154255 | sWalker.xExprCallback = codeCursorHintFixExpr; |
| 154256 | if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr); |
| 154257 | sqlite3VdbeAddOp4(v, OP_CursorHint, |
| 154258 | (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, |
| 154259 | (const char*)pExpr, P4_EXPR); |
| 154260 | } |
| 154261 | } |
| @@ -156836,11 +157117,11 @@ | |
| 157117 | if( op==TK_ISNULL |
| 157118 | && !ExprHasProperty(pExpr,EP_OuterON) |
| 157119 | && 0==sqlite3ExprCanBeNull(pLeft) |
| 157120 | ){ |
| 157121 | assert( !ExprHasProperty(pExpr, EP_IntValue) ); |
| 157122 | pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */ |
| 157123 | pExpr->u.zToken = "false"; |
| 157124 | ExprSetProperty(pExpr, EP_IsFalse); |
| 157125 | pTerm->prereqAll = 0; |
| 157126 | pTerm->eOperator = 0; |
| 157127 | } |
| @@ -158368,12 +158649,11 @@ | |
| 158649 | ** and to set up the WhereLevel object pLevel so that the code generator |
| 158650 | ** makes use of the automatic index. |
| 158651 | */ |
| 158652 | static SQLITE_NOINLINE void constructAutomaticIndex( |
| 158653 | Parse *pParse, /* The parsing context */ |
| 158654 | WhereClause *pWC, /* The WHERE clause */ |
| 158655 | const Bitmask notReady, /* Mask of cursors that are not available */ |
| 158656 | WhereLevel *pLevel /* Write new index here */ |
| 158657 | ){ |
| 158658 | int nKeyCol; /* Number of columns in the constructed index */ |
| 158659 | WhereTerm *pTerm; /* A single term of the WHERE clause */ |
| @@ -158394,11 +158674,12 @@ | |
| 158674 | Bitmask extraCols; /* Bitmap of additional columns */ |
| 158675 | u8 sentWarning = 0; /* True if a warning has been issued */ |
| 158676 | u8 useBloomFilter = 0; /* True to also add a Bloom filter */ |
| 158677 | Expr *pPartial = 0; /* Partial Index Expression */ |
| 158678 | int iContinue = 0; /* Jump here to skip excluded rows */ |
| 158679 | SrcList *pTabList; /* The complete FROM clause */ |
| 158680 | SrcItem *pSrc; /* The FROM clause term to get the next index */ |
| 158681 | int addrCounter = 0; /* Address where integer counter is initialized */ |
| 158682 | int regBase; /* Array of registers where record is assembled */ |
| 158683 | #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
| 158684 | int addrExp = 0; /* Address of OP_Explain */ |
| 158685 | #endif |
| @@ -158410,10 +158691,12 @@ | |
| 158691 | addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158692 | |
| 158693 | /* Count the number of columns that will be added to the index |
| 158694 | ** and used to match WHERE clause constraints */ |
| 158695 | nKeyCol = 0; |
| 158696 | pTabList = pWC->pWInfo->pTabList; |
| 158697 | pSrc = &pTabList->a[pLevel->iFrom]; |
| 158698 | pTable = pSrc->pTab; |
| 158699 | pWCEnd = &pWC->a[pWC->nTerm]; |
| 158700 | pLoop = pLevel->pWLoop; |
| 158701 | idxCols = 0; |
| 158702 | for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ |
| @@ -158420,11 +158703,11 @@ | |
| 158703 | Expr *pExpr = pTerm->pExpr; |
| 158704 | /* Make the automatic index a partial index if there are terms in the |
| 158705 | ** WHERE clause (or the ON clause of a LEFT join) that constrain which |
| 158706 | ** rows of the target table (pSrc) that can be used. */ |
| 158707 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158708 | && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) |
| 158709 | ){ |
| 158710 | pPartial = sqlite3ExprAnd(pParse, pPartial, |
| 158711 | sqlite3ExprDup(pParse->db, pExpr, 0)); |
| 158712 | } |
| 158713 | if( termCanDriveIndex(pTerm, pSrc, notReady) ){ |
| @@ -158550,18 +158833,18 @@ | |
| 158833 | pLevel->regFilter = ++pParse->nMem; |
| 158834 | sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter); |
| 158835 | } |
| 158836 | |
| 158837 | /* Fill the automatic index with content */ |
| 158838 | assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] ); |
| 158839 | if( pSrc->fg.viaCoroutine ){ |
| 158840 | int regYield = pSrc->regReturn; |
| 158841 | addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); |
| 158842 | sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub); |
| 158843 | addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); |
| 158844 | VdbeCoverage(v); |
| 158845 | VdbeComment((v, "next row of %s", pSrc->pTab->zName)); |
| 158846 | }else{ |
| 158847 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); |
| 158848 | } |
| 158849 | if( pPartial ){ |
| 158850 | iContinue = sqlite3VdbeMakeLabel(pParse); |
| @@ -158578,18 +158861,18 @@ | |
| 158861 | } |
| 158862 | sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v)); |
| 158863 | sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); |
| 158864 | sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); |
| 158865 | if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); |
| 158866 | if( pSrc->fg.viaCoroutine ){ |
| 158867 | sqlite3VdbeChangeP2(v, addrCounter, regBase+n); |
| 158868 | testcase( pParse->db->mallocFailed ); |
| 158869 | assert( pLevel->iIdxCur>0 ); |
| 158870 | translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, |
| 158871 | pSrc->regResult, pLevel->iIdxCur); |
| 158872 | sqlite3VdbeGoto(v, addrTop); |
| 158873 | pSrc->fg.viaCoroutine = 0; |
| 158874 | }else{ |
| 158875 | sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); |
| 158876 | sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); |
| 158877 | } |
| 158878 | sqlite3VdbeJumpHere(v, addrTop); |
| @@ -158648,13 +158931,15 @@ | |
| 158931 | assert( v!=0 ); |
| 158932 | assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); |
| 158933 | |
| 158934 | addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 158935 | do{ |
| 158936 | const SrcList *pTabList; |
| 158937 | const SrcItem *pItem; |
| 158938 | const Table *pTab; |
| 158939 | u64 sz; |
| 158940 | int iSrc; |
| 158941 | sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel); |
| 158942 | addrCont = sqlite3VdbeMakeLabel(pParse); |
| 158943 | iCur = pLevel->iTabCur; |
| 158944 | pLevel->regFilter = ++pParse->nMem; |
| 158945 | |
| @@ -158664,11 +158949,13 @@ | |
| 158949 | ** measure the size of the table at run-time using OP_Count with |
| 158950 | ** P3==1 and use that value to initialize the blob. But that makes |
| 158951 | ** testing complicated. By basing the blob size on the value in the |
| 158952 | ** sqlite_stat1 table, testing is much easier. |
| 158953 | */ |
| 158954 | pTabList = pWInfo->pTabList; |
| 158955 | iSrc = pLevel->iFrom; |
| 158956 | pItem = &pTabList->a[iSrc]; |
| 158957 | assert( pItem!=0 ); |
| 158958 | pTab = pItem->pTab; |
| 158959 | assert( pTab!=0 ); |
| 158960 | sz = sqlite3LogEstToInt(pTab->nRowLogEst); |
| 158961 | if( sz<10000 ){ |
| @@ -158681,11 +158968,11 @@ | |
| 158968 | addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); |
| 158969 | pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm]; |
| 158970 | for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){ |
| 158971 | Expr *pExpr = pTerm->pExpr; |
| 158972 | if( (pTerm->wtFlags & TERM_VIRTUAL)==0 |
| 158973 | && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) |
| 158974 | ){ |
| 158975 | sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); |
| 158976 | } |
| 158977 | } |
| 158978 | if( pLoop->wsFlags & WHERE_IPK ){ |
| @@ -163020,10 +163307,17 @@ | |
| 163307 | ** 3) Either the query must be DISTINCT, or else the ON or USING clause |
| 163308 | ** must contain a constraint that limits the scan of the table to |
| 163309 | ** at most a single row. |
| 163310 | ** 4) The table must not be referenced by any part of the query apart |
| 163311 | ** from its own USING or ON clause. |
| 163312 | ** 5) The table must not have an inner-join ON or USING clause if there is |
| 163313 | ** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause |
| 163314 | ** might move from the right side to the left side of the RIGHT JOIN. |
| 163315 | ** Note: Due to (2), this condition can only arise if the table is |
| 163316 | ** the right-most table of a subquery that was flattened into the |
| 163317 | ** main query and that subquery was the right-hand operand of an |
| 163318 | ** inner join that held an ON or USING clause. |
| 163319 | ** |
| 163320 | ** For example, given: |
| 163321 | ** |
| 163322 | ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); |
| 163323 | ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); |
| @@ -163045,10 +163339,11 @@ | |
| 163339 | WhereInfo *pWInfo, |
| 163340 | Bitmask notReady |
| 163341 | ){ |
| 163342 | int i; |
| 163343 | Bitmask tabUsed; |
| 163344 | int hasRightJoin; |
| 163345 | |
| 163346 | /* Preconditions checked by the caller */ |
| 163347 | assert( pWInfo->nLevel>=2 ); |
| 163348 | assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) ); |
| 163349 | |
| @@ -163059,10 +163354,11 @@ | |
| 163354 | |
| 163355 | tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet); |
| 163356 | if( pWInfo->pOrderBy ){ |
| 163357 | tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy); |
| 163358 | } |
| 163359 | hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0; |
| 163360 | for(i=pWInfo->nLevel-1; i>=1; i--){ |
| 163361 | WhereTerm *pTerm, *pEnd; |
| 163362 | SrcItem *pItem; |
| 163363 | WhereLoop *pLoop; |
| 163364 | pLoop = pWInfo->a[i].pWLoop; |
| @@ -163081,10 +163377,16 @@ | |
| 163377 | || pTerm->pExpr->w.iJoin!=pItem->iCursor |
| 163378 | ){ |
| 163379 | break; |
| 163380 | } |
| 163381 | } |
| 163382 | if( hasRightJoin |
| 163383 | && ExprHasProperty(pTerm->pExpr, EP_InnerON) |
| 163384 | && pTerm->pExpr->w.iJoin==pItem->iCursor |
| 163385 | ){ |
| 163386 | break; /* restriction (5) */ |
| 163387 | } |
| 163388 | } |
| 163389 | if( pTerm<pEnd ) continue; |
| 163390 | WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId)); |
| 163391 | notReady &= ~pLoop->maskSelf; |
| 163392 | for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ |
| @@ -163480,26 +163782,49 @@ | |
| 163782 | if( pSelect && pSelect->pLimit ){ |
| 163783 | sqlite3WhereAddLimit(&pWInfo->sWC, pSelect); |
| 163784 | } |
| 163785 | if( pParse->nErr ) goto whereBeginError; |
| 163786 | |
| 163787 | /* The False-WHERE-Term-Bypass optimization: |
| 163788 | ** |
| 163789 | ** If there are WHERE terms that are false, then no rows will be output, |
| 163790 | ** so skip over all of the code generated here. |
| 163791 | ** |
| 163792 | ** Conditions: |
| 163793 | ** |
| 163794 | ** (1) The WHERE term must not refer to any tables in the join. |
| 163795 | ** (2) The term must not come from an ON clause on the |
| 163796 | ** right-hand side of a LEFT or FULL JOIN. |
| 163797 | ** (3) The term must not come from an ON clause, or there must be |
| 163798 | ** no RIGHT or FULL OUTER joins in pTabList. |
| 163799 | ** (4) If the expression contains non-deterministic functions |
| 163800 | ** that are not within a sub-select. This is not required |
| 163801 | ** for correctness but rather to preserves SQLite's legacy |
| 163802 | ** behaviour in the following two cases: |
| 163803 | ** |
| 163804 | ** WHERE random()>0; -- eval random() once per row |
| 163805 | ** WHERE (SELECT random())>0; -- eval random() just once overall |
| 163806 | ** |
| 163807 | ** Note that the Where term need not be a constant in order for this |
| 163808 | ** optimization to apply, though it does need to be constant relative to |
| 163809 | ** the current subquery (condition 1). The term might include variables |
| 163810 | ** from outer queries so that the value of the term changes from one |
| 163811 | ** invocation of the current subquery to the next. |
| 163812 | */ |
| 163813 | for(ii=0; ii<sWLB.pWC->nBase; ii++){ |
| 163814 | WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */ |
| 163815 | Expr *pX; /* The expression of pT */ |
| 163816 | if( pT->wtFlags & TERM_VIRTUAL ) continue; |
| 163817 | pX = pT->pExpr; |
| 163818 | assert( pX!=0 ); |
| 163819 | assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) ); |
| 163820 | if( pT->prereqAll==0 /* Conditions (1) and (2) */ |
| 163821 | && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */ |
| 163822 | && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */ |
| 163823 | && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 ) |
| 163824 | ){ |
| 163825 | sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL); |
| 163826 | pT->wtFlags |= TERM_CODED; |
| 163827 | } |
| 163828 | } |
| 163829 | |
| 163830 | if( wctrlFlags & WHERE_WANT_DISTINCT ){ |
| @@ -163875,15 +164200,15 @@ | |
| 164200 | int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); |
| 164201 | sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); |
| 164202 | sqlite3VdbeJumpHere(v, iOnce); |
| 164203 | } |
| 164204 | } |
| 164205 | assert( pTabList == pWInfo->pTabList ); |
| 164206 | if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ |
| 164207 | if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ |
| 164208 | #ifndef SQLITE_OMIT_AUTOMATIC_INDEX |
| 164209 | constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel); |
| 164210 | #endif |
| 164211 | }else{ |
| 164212 | sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady); |
| 164213 | } |
| 164214 | if( db->mallocFailed ) goto whereBeginError; |
| @@ -165072,10 +165397,11 @@ | |
| 165397 | } |
| 165398 | } |
| 165399 | } |
| 165400 | /* no break */ deliberate_fall_through |
| 165401 | |
| 165402 | case TK_IF_NULL_ROW: |
| 165403 | case TK_AGG_FUNCTION: |
| 165404 | case TK_COLUMN: { |
| 165405 | int iCol = -1; |
| 165406 | if( pParse->db->mallocFailed ) return WRC_Abort; |
| 165407 | if( p->pSub ){ |
| @@ -167902,11 +168228,11 @@ | |
| 168228 | #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; |
| 168229 | #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; |
| 168230 | #define YYFALLBACK 1 |
| 168231 | #define YYNSTATE 575 |
| 168232 | #define YYNRULE 403 |
| 168233 | #define YYNRULE_WITH_ACTION 340 |
| 168234 | #define YYNTOKEN 185 |
| 168235 | #define YY_MAX_SHIFT 574 |
| 168236 | #define YY_MIN_SHIFTREDUCE 833 |
| 168237 | #define YY_MAX_SHIFTREDUCE 1235 |
| 168238 | #define YY_ERROR_ACTION 1236 |
| @@ -167982,145 +168308,145 @@ | |
| 168308 | *********** Begin parsing tables **********************************************/ |
| 168309 | #define YY_ACTTAB_COUNT (2096) |
| 168310 | static const YYACTIONTYPE yy_action[] = { |
| 168311 | /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, |
| 168312 | /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, |
| 168313 | /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71, |
| 168314 | /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, |
| 168315 | /* 40 */ 397, 71, 71, 125, 126, 80, 1212, 1212, 1047, 1050, |
| 168316 | /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, |
| 168317 | /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, |
| 168318 | /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, |
| 168319 | /* 80 */ 417, 523, 142, 125, 126, 80, 1212, 1212, 1047, 1050, |
| 168320 | /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, |
| 168321 | /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, |
| 168322 | /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, |
| 168323 | /* 120 */ 442, 1561, 376, 1563, 1188, 375, 1159, 565, 1159, 565, |
| 168324 | /* 130 */ 409, 1561, 537, 259, 226, 444, 101, 145, 449, 316, |
| 168325 | /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, |
| 168326 | /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1212, 1212, 1047, |
| 168327 | /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, |
| 168328 | /* 170 */ 294, 1188, 339, 448, 120, 120, 120, 119, 116, 444, |
| 168329 | /* 180 */ 127, 1188, 1189, 1188, 148, 441, 440, 568, 119, 116, |
| 168330 | /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, |
| 168331 | /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, |
| 168332 | /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, |
| 168333 | /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1188, 1189, |
| 168334 | /* 230 */ 1188, 149, 1220, 409, 1220, 124, 124, 124, 124, 122, |
| 168335 | /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168336 | /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, |
| 168337 | /* 260 */ 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, |
| 168338 | /* 270 */ 124, 124, 1275, 522, 222, 1188, 568, 409, 224, 514, |
| 168339 | /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, |
| 168340 | /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1188, 133, |
| 168341 | /* 300 */ 133, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, |
| 168342 | /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, |
| 168343 | /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, |
| 168344 | /* 330 */ 1188, 373, 1188, 1189, 1188, 252, 1429, 399, 504, 501, |
| 168345 | /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, |
| 168346 | /* 350 */ 460, 328, 360, 394, 1233, 1188, 1189, 1188, 563, 568, |
| 168347 | /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, |
| 168348 | /* 370 */ 116, 444, 284, 284, 369, 1574, 1600, 441, 440, 154, |
| 168349 | /* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1188, 1189, 1188, |
| 168350 | /* 390 */ 85, 1219, 271, 557, 543, 515, 1555, 568, 98, 1218, |
| 168351 | /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1212, 1212, 1047, |
| 168352 | /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, |
| 168353 | /* 420 */ 13, 13, 1024, 507, 1220, 1188, 1220, 549, 109, 109, |
| 168354 | /* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569, |
| 168355 | /* 440 */ 445, 430, 1546, 1014, 325, 551, 1188, 270, 287, 368, |
| 168356 | /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, |
| 168357 | /* 460 */ 316, 559, 1606, 122, 122, 122, 122, 121, 121, 120, |
| 168358 | /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, |
| 168359 | /* 480 */ 284, 284, 1188, 1189, 1188, 1154, 568, 1605, 409, 899, |
| 168360 | /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1154, 516, |
| 168361 | /* 500 */ 413, 1154, 552, 1188, 1189, 1188, 568, 544, 1548, 51, |
| 168362 | /* 510 */ 51, 214, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, |
| 168363 | /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1188, 474, 135, |
| 168364 | /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120, |
| 168365 | /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1555, |
| 168366 | /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1212, 1212, |
| 168367 | /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168368 | /* 570 */ 1549, 122, 122, 122, 122, 121, 121, 120, 120, 120, |
| 168369 | /* 580 */ 119, 116, 444, 485, 1188, 1189, 1188, 482, 281, 1263, |
| 168370 | /* 590 */ 955, 252, 1188, 373, 504, 501, 500, 1188, 340, 570, |
| 168371 | /* 600 */ 1188, 570, 409, 292, 499, 955, 874, 191, 480, 316, |
| 168372 | /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, |
| 168373 | /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, |
| 168374 | /* 630 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168375 | /* 640 */ 124, 409, 394, 1132, 1188, 867, 100, 284, 284, 1188, |
| 168376 | /* 650 */ 1189, 1188, 373, 1089, 1188, 1189, 1188, 1188, 1189, 1188, |
| 168377 | /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1212, 1212, |
| 168378 | /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168379 | /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, |
| 168380 | /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1154, 228, 1188, |
| 168381 | /* 700 */ 157, 1188, 1189, 1188, 1547, 13, 13, 301, 955, 1228, |
| 168382 | /* 710 */ 1154, 153, 409, 1154, 373, 1577, 1172, 5, 369, 1574, |
| 168383 | /* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121, |
| 168384 | /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, |
| 168385 | /* 740 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168386 | /* 750 */ 124, 409, 208, 567, 1188, 1025, 1188, 1189, 1188, 1188, |
| 168387 | /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568, |
| 168388 | /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1212, 1212, |
| 168389 | /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168390 | /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, |
| 168391 | /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, |
| 168392 | /* 810 */ 528, 1188, 1189, 1188, 13, 13, 1188, 1189, 1188, 1293, |
| 168393 | /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200, |
| 168394 | /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, |
| 168395 | /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, |
| 168396 | /* 850 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168397 | /* 860 */ 124, 409, 227, 1069, 1154, 284, 284, 419, 312, 278, |
| 168398 | /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1154, 565, 568, |
| 168399 | /* 880 */ 1154, 1191, 565, 1594, 565, 125, 126, 80, 1212, 1212, |
| 168400 | /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, |
| 168401 | /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121, |
| 168402 | /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, |
| 168403 | /* 920 */ 1580, 574, 2, 1241, 838, 839, 840, 1556, 317, 1207, |
| 168404 | /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1191, |
| 168405 | /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, |
| 168406 | /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212, |
| 168407 | /* 960 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168408 | /* 970 */ 124, 568, 284, 284, 568, 1208, 409, 573, 313, 1241, |
| 168409 | /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637, |
| 168410 | /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, |
| 168411 | /* 1000 */ 1321, 104, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, |
| 168412 | /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, |
| 168413 | /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284, |
| 168414 | /* 1030 */ 428, 448, 1519, 1208, 439, 284, 284, 1483, 1348, 311, |
| 168415 | /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532, |
| 168416 | /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122, |
| 168417 | /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, |
| 168418 | /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568, |
| 168419 | /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322, |
| 168420 | /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491, |
| 168421 | /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, |
| 168422 | /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1212, |
| 168423 | /* 1120 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168424 | /* 1130 */ 124, 347, 409, 862, 1528, 1208, 125, 126, 80, 1212, |
| 168425 | /* 1140 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168426 | /* 1150 */ 124, 1133, 1635, 474, 1635, 371, 125, 114, 80, 1212, |
| 168427 | /* 1160 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, |
| 168428 | /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121, |
| 168429 | /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, |
| 168430 | /* 1190 */ 1290, 862, 464, 1208, 436, 122, 122, 122, 122, 121, |
| 168431 | /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1636, |
| 168432 | /* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121, |
| 168433 | /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, |
| 168434 | /* 1230 */ 1131, 1415, 1553, 1554, 1327, 409, 6, 6, 1165, 1264, |
| 168435 | /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, |
| 168436 | /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, |
| 168437 | /* 1260 */ 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, |
| 168438 | /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1188, 1415, |
| 168439 | /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1552, 847, |
| 168440 | /* 1290 */ 1165, 407, 6, 568, 321, 1154, 470, 44, 44, 1551, |
| 168441 | /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1154, 431, |
| 168442 | /* 1310 */ 568, 1154, 322, 17, 487, 1111, 58, 58, 122, 122, |
| 168443 | /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, |
| 168444 | /* 1330 */ 1112, 216, 481, 59, 59, 1188, 1189, 1188, 111, 560, |
| 168445 | /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, |
| 168446 | /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091, |
| 168447 | /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60, |
| 168448 | /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, |
| 168449 | /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, |
| 168450 | /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, |
| 168451 | /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534, |
| 168452 | /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024, |
| @@ -168127,73 +168453,73 @@ | |
| 168453 | /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66, |
| 168454 | /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177, |
| 168455 | /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, |
| 168456 | /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, |
| 168457 | /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, |
| 168458 | /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1579, 1176, 447, |
| 168459 | /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391, |
| 168460 | /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, |
| 168461 | /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323, |
| 168462 | /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, |
| 168463 | /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, |
| 168464 | /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483, |
| 168465 | /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557, |
| 168466 | /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, |
| 168467 | /* 1560 */ 161, 1568, 557, 535, 568, 319, 568, 348, 536, 1007, |
| 168468 | /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, |
| 168469 | /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, |
| 168470 | /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, |
| 168471 | /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445, |
| 168472 | /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, |
| 168473 | /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, |
| 168474 | /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, |
| 168475 | /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1176, |
| 168476 | /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, |
| 168477 | /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258, |
| 168478 | /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, |
| 168479 | /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74, |
| 168480 | /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, |
| 168481 | /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342, |
| 168482 | /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, |
| 168483 | /* 1720 */ 1251, 445, 1587, 1339, 308, 276, 168, 309, 11, 141, |
| 168484 | /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, |
| 168485 | /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, |
| 168486 | /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, |
| 168487 | /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109, |
| 168488 | /* 1770 */ 204, 1590, 1228, 558, 265, 218, 110, 205, 445, 569, |
| 168489 | /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014, |
| 168490 | /* 1790 */ 1016, 1017, 27, 230, 1525, 1225, 79, 560, 85, 4, |
| 168491 | /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, |
| 168492 | /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, |
| 168493 | /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36, |
| 168494 | /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246, |
| 168495 | /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350, |
| 168496 | /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, |
| 168497 | /* 1860 */ 1307, 1300, 93, 1604, 881, 1603, 224, 404, 434, 520, |
| 168498 | /* 1870 */ 263, 435, 1573, 563, 1279, 1278, 364, 1024, 306, 1277, |
| 168499 | /* 1880 */ 264, 1602, 1559, 109, 109, 370, 1299, 307, 1558, 438, |
| 168500 | /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, |
| 168501 | /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, |
| 168502 | /* 1910 */ 1182, 530, 272, 274, 379, 210, 1331, 547, 385, 386, |
| 168503 | /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513, |
| 168504 | /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147, |
| 168505 | /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, |
| 168506 | /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084, |
| 168507 | /* 1960 */ 326, 180, 169, 1207, 182, 334, 238, 913, 241, 1100, |
| 168508 | /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, |
| 168509 | /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247, |
| 168510 | /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1222, 489, |
| 168511 | /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, |
| 168512 | /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, |
| 168513 | /* 2020 */ 513, 39, 95, 1170, 160, 1053, 964, 1139, 96, 174, |
| 168514 | /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1160, 1156, 260, |
| 168515 | /* 2040 */ 21, 22, 23, 1158, 1164, 1163, 1144, 24, 33, 25, |
| 168516 | /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052, |
| 168517 | /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019, |
| 168518 | /* 2070 */ 861, 112, 29, 564, 1178, 1177, 268, 176, 143, 923, |
| 168519 | /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, |
| 168520 | /* 2090 */ 1238, 1238, 1238, 1238, 269, 1595, |
| 168521 | }; |
| 168522 | static const YYCODETYPE yy_lookahead[] = { |
| 168523 | /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, |
| 168524 | /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, |
| 168525 | /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, |
| @@ -168532,67 +168858,67 @@ | |
| 168858 | /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718, |
| 168859 | /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716, |
| 168860 | /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, |
| 168861 | }; |
| 168862 | static const YYACTIONTYPE yy_default[] = { |
| 168863 | /* 0 */ 1641, 1641, 1641, 1469, 1236, 1347, 1236, 1236, 1236, 1469, |
| 168864 | /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236, |
| 168865 | /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236, |
| 168866 | /* 30 */ 1236, 1236, 1557, 1557, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168867 | /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, |
| 168868 | /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399, |
| 168869 | /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464, |
| 168870 | /* 70 */ 1619, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, |
| 168871 | /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168872 | /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168873 | /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168874 | /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168875 | /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168876 | /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, |
| 168877 | /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, |
| 168878 | /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427, |
| 168879 | /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1593, 1592, 1487, 1236, |
| 168880 | /* 170 */ 1236, 1236, 1236, 1236, 1236, 1557, 1236, 1236, 1236, 1236, |
| 168881 | /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168882 | /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, |
| 168883 | /* 200 */ 1557, 1557, 1236, 1269, 1557, 1557, 1368, 1368, 1265, 1265, |
| 168884 | /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236, |
| 168885 | /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168886 | /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236, |
| 168887 | /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168888 | /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168889 | /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168890 | /* 270 */ 1236, 1236, 1236, 1236, 1236, 1586, 1236, 1499, 1325, 1343, |
| 168891 | /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633, |
| 168892 | /* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403, |
| 168893 | /* 300 */ 1344, 1630, 1286, 1608, 1281, 1377, 1377, 1377, 1367, 1367, |
| 168894 | /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633, |
| 168895 | /* 320 */ 1353, 1353, 1632, 1632, 1353, 1487, 1616, 1412, 1314, 1320, |
| 168896 | /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1616, 1616, 1390, 1412, |
| 168897 | /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1627, 1353, 1254, |
| 168898 | /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301, |
| 168899 | /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1575, |
| 168900 | /* 370 */ 1236, 1481, 1481, 1477, 1353, 1567, 1567, 1380, 1380, 1385, |
| 168901 | /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1589, |
| 168902 | /* 390 */ 1589, 1585, 1585, 1585, 1638, 1638, 1536, 1601, 1269, 1269, |
| 168903 | /* 400 */ 1269, 1269, 1601, 1288, 1288, 1270, 1270, 1269, 1601, 1236, |
| 168904 | /* 410 */ 1236, 1236, 1236, 1236, 1236, 1596, 1236, 1531, 1488, 1357, |
| 168905 | /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168906 | /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236, |
| 168907 | /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236, |
| 168908 | /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, |
| 168909 | /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, |
| 168910 | /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168911 | /* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, |
| 168912 | /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168913 | /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, |
| 168914 | /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168915 | /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, |
| 168916 | /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168917 | /* 540 */ 1236, 1236, 1236, 1236, 1572, 1372, 1236, 1236, 1236, 1236, |
| 168918 | /* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, |
| 168919 | /* 560 */ 1236, 1236, 1236, 1236, 1236, 1612, 1328, 1418, 1236, 1421, |
| 168920 | /* 570 */ 1258, 1236, 1248, 1236, 1236, |
| 168921 | }; |
| 168922 | /********** End of lemon-generated parsing tables *****************************/ |
| 168923 | |
| 168924 | /* The next table maps tokens (terminal symbols) into fallback tokens. |
| @@ -169437,162 +169763,162 @@ | |
| 169763 | /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", |
| 169764 | /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", |
| 169765 | /* 226 */ "case_exprlist ::= WHEN expr THEN expr", |
| 169766 | /* 227 */ "case_else ::= ELSE expr", |
| 169767 | /* 228 */ "case_else ::=", |
| 169768 | /* 229 */ "case_operand ::=", |
| 169769 | /* 230 */ "exprlist ::=", |
| 169770 | /* 231 */ "nexprlist ::= nexprlist COMMA expr", |
| 169771 | /* 232 */ "nexprlist ::= expr", |
| 169772 | /* 233 */ "paren_exprlist ::=", |
| 169773 | /* 234 */ "paren_exprlist ::= LP exprlist RP", |
| 169774 | /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", |
| 169775 | /* 236 */ "uniqueflag ::= UNIQUE", |
| 169776 | /* 237 */ "uniqueflag ::=", |
| 169777 | /* 238 */ "eidlist_opt ::=", |
| 169778 | /* 239 */ "eidlist_opt ::= LP eidlist RP", |
| 169779 | /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", |
| 169780 | /* 241 */ "eidlist ::= nm collate sortorder", |
| 169781 | /* 242 */ "collate ::=", |
| 169782 | /* 243 */ "collate ::= COLLATE ID|STRING", |
| 169783 | /* 244 */ "cmd ::= DROP INDEX ifexists fullname", |
| 169784 | /* 245 */ "cmd ::= VACUUM vinto", |
| 169785 | /* 246 */ "cmd ::= VACUUM nm vinto", |
| 169786 | /* 247 */ "vinto ::= INTO expr", |
| 169787 | /* 248 */ "vinto ::=", |
| 169788 | /* 249 */ "cmd ::= PRAGMA nm dbnm", |
| 169789 | /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", |
| 169790 | /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", |
| 169791 | /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", |
| 169792 | /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", |
| 169793 | /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", |
| 169794 | /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", |
| 169795 | /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", |
| 169796 | /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", |
| 169797 | /* 258 */ "trigger_time ::= BEFORE|AFTER", |
| 169798 | /* 259 */ "trigger_time ::= INSTEAD OF", |
| 169799 | /* 260 */ "trigger_time ::=", |
| 169800 | /* 261 */ "trigger_event ::= DELETE|INSERT", |
| 169801 | /* 262 */ "trigger_event ::= UPDATE", |
| 169802 | /* 263 */ "trigger_event ::= UPDATE OF idlist", |
| 169803 | /* 264 */ "when_clause ::=", |
| 169804 | /* 265 */ "when_clause ::= WHEN expr", |
| 169805 | /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", |
| 169806 | /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", |
| 169807 | /* 268 */ "trnm ::= nm DOT nm", |
| 169808 | /* 269 */ "tridxby ::= INDEXED BY nm", |
| 169809 | /* 270 */ "tridxby ::= NOT INDEXED", |
| 169810 | /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", |
| 169811 | /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", |
| 169812 | /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", |
| 169813 | /* 274 */ "trigger_cmd ::= scanpt select scanpt", |
| 169814 | /* 275 */ "expr ::= RAISE LP IGNORE RP", |
| 169815 | /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", |
| 169816 | /* 277 */ "raisetype ::= ROLLBACK", |
| 169817 | /* 278 */ "raisetype ::= ABORT", |
| 169818 | /* 279 */ "raisetype ::= FAIL", |
| 169819 | /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", |
| 169820 | /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", |
| 169821 | /* 282 */ "cmd ::= DETACH database_kw_opt expr", |
| 169822 | /* 283 */ "key_opt ::=", |
| 169823 | /* 284 */ "key_opt ::= KEY expr", |
| 169824 | /* 285 */ "cmd ::= REINDEX", |
| 169825 | /* 286 */ "cmd ::= REINDEX nm dbnm", |
| 169826 | /* 287 */ "cmd ::= ANALYZE", |
| 169827 | /* 288 */ "cmd ::= ANALYZE nm dbnm", |
| 169828 | /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", |
| 169829 | /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", |
| 169830 | /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", |
| 169831 | /* 292 */ "add_column_fullname ::= fullname", |
| 169832 | /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", |
| 169833 | /* 294 */ "cmd ::= create_vtab", |
| 169834 | /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", |
| 169835 | /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", |
| 169836 | /* 297 */ "vtabarg ::=", |
| 169837 | /* 298 */ "vtabargtoken ::= ANY", |
| 169838 | /* 299 */ "vtabargtoken ::= lp anylist RP", |
| 169839 | /* 300 */ "lp ::= LP", |
| 169840 | /* 301 */ "with ::= WITH wqlist", |
| 169841 | /* 302 */ "with ::= WITH RECURSIVE wqlist", |
| 169842 | /* 303 */ "wqas ::= AS", |
| 169843 | /* 304 */ "wqas ::= AS MATERIALIZED", |
| 169844 | /* 305 */ "wqas ::= AS NOT MATERIALIZED", |
| 169845 | /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", |
| 169846 | /* 307 */ "wqlist ::= wqitem", |
| 169847 | /* 308 */ "wqlist ::= wqlist COMMA wqitem", |
| 169848 | /* 309 */ "windowdefn_list ::= windowdefn", |
| 169849 | /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", |
| 169850 | /* 311 */ "windowdefn ::= nm AS LP window RP", |
| 169851 | /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", |
| 169852 | /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", |
| 169853 | /* 314 */ "window ::= ORDER BY sortlist frame_opt", |
| 169854 | /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", |
| 169855 | /* 316 */ "window ::= frame_opt", |
| 169856 | /* 317 */ "window ::= nm frame_opt", |
| 169857 | /* 318 */ "frame_opt ::=", |
| 169858 | /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", |
| 169859 | /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", |
| 169860 | /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", |
| 169861 | /* 322 */ "frame_bound_s ::= frame_bound", |
| 169862 | /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", |
| 169863 | /* 324 */ "frame_bound_e ::= frame_bound", |
| 169864 | /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", |
| 169865 | /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", |
| 169866 | /* 327 */ "frame_bound ::= CURRENT ROW", |
| 169867 | /* 328 */ "frame_exclude_opt ::=", |
| 169868 | /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", |
| 169869 | /* 330 */ "frame_exclude ::= NO OTHERS", |
| 169870 | /* 331 */ "frame_exclude ::= CURRENT ROW", |
| 169871 | /* 332 */ "frame_exclude ::= GROUP|TIES", |
| 169872 | /* 333 */ "window_clause ::= WINDOW windowdefn_list", |
| 169873 | /* 334 */ "filter_over ::= filter_clause over_clause", |
| 169874 | /* 335 */ "filter_over ::= over_clause", |
| 169875 | /* 336 */ "filter_over ::= filter_clause", |
| 169876 | /* 337 */ "over_clause ::= OVER LP window RP", |
| 169877 | /* 338 */ "over_clause ::= OVER nm", |
| 169878 | /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", |
| 169879 | /* 340 */ "input ::= cmdlist", |
| 169880 | /* 341 */ "cmdlist ::= cmdlist ecmd", |
| 169881 | /* 342 */ "cmdlist ::= ecmd", |
| 169882 | /* 343 */ "ecmd ::= SEMI", |
| 169883 | /* 344 */ "ecmd ::= cmdx SEMI", |
| 169884 | /* 345 */ "ecmd ::= explain cmdx SEMI", |
| 169885 | /* 346 */ "trans_opt ::=", |
| 169886 | /* 347 */ "trans_opt ::= TRANSACTION", |
| 169887 | /* 348 */ "trans_opt ::= TRANSACTION nm", |
| 169888 | /* 349 */ "savepoint_opt ::= SAVEPOINT", |
| 169889 | /* 350 */ "savepoint_opt ::=", |
| 169890 | /* 351 */ "cmd ::= create_table create_table_args", |
| 169891 | /* 352 */ "table_option_set ::= table_option", |
| 169892 | /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", |
| 169893 | /* 354 */ "columnlist ::= columnname carglist", |
| 169894 | /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", |
| 169895 | /* 356 */ "nm ::= STRING", |
| 169896 | /* 357 */ "typetoken ::= typename", |
| 169897 | /* 358 */ "typename ::= ID|STRING", |
| 169898 | /* 359 */ "signed ::= plus_num", |
| 169899 | /* 360 */ "signed ::= minus_num", |
| 169900 | /* 361 */ "carglist ::= carglist ccons", |
| 169901 | /* 362 */ "carglist ::=", |
| 169902 | /* 363 */ "ccons ::= NULL onconf", |
| 169903 | /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", |
| 169904 | /* 365 */ "ccons ::= AS generated", |
| 169905 | /* 366 */ "conslist_opt ::= COMMA conslist", |
| 169906 | /* 367 */ "conslist ::= conslist tconscomma tcons", |
| 169907 | /* 368 */ "conslist ::= tcons", |
| 169908 | /* 369 */ "tconscomma ::=", |
| 169909 | /* 370 */ "defer_subclause_opt ::= defer_subclause", |
| 169910 | /* 371 */ "resolvetype ::= raisetype", |
| 169911 | /* 372 */ "selectnowith ::= oneselect", |
| 169912 | /* 373 */ "oneselect ::= values", |
| 169913 | /* 374 */ "sclp ::= selcollist COMMA", |
| 169914 | /* 375 */ "as ::= ID|STRING", |
| 169915 | /* 376 */ "indexed_opt ::= indexed_by", |
| 169916 | /* 377 */ "returning ::=", |
| 169917 | /* 378 */ "expr ::= term", |
| 169918 | /* 379 */ "likeop ::= LIKE_KW|MATCH", |
| 169919 | /* 380 */ "case_operand ::= expr", |
| 169920 | /* 381 */ "exprlist ::= nexprlist", |
| 169921 | /* 382 */ "nmnum ::= plus_num", |
| 169922 | /* 383 */ "nmnum ::= nm", |
| 169923 | /* 384 */ "nmnum ::= ON", |
| 169924 | /* 385 */ "nmnum ::= DELETE", |
| @@ -170346,162 +170672,162 @@ | |
| 170672 | 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 170673 | 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 170674 | 279, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 170675 | 280, /* (227) case_else ::= ELSE expr */ |
| 170676 | 280, /* (228) case_else ::= */ |
| 170677 | 278, /* (229) case_operand ::= */ |
| 170678 | 261, /* (230) exprlist ::= */ |
| 170679 | 253, /* (231) nexprlist ::= nexprlist COMMA expr */ |
| 170680 | 253, /* (232) nexprlist ::= expr */ |
| 170681 | 277, /* (233) paren_exprlist ::= */ |
| 170682 | 277, /* (234) paren_exprlist ::= LP exprlist RP */ |
| 170683 | 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 170684 | 281, /* (236) uniqueflag ::= UNIQUE */ |
| 170685 | 281, /* (237) uniqueflag ::= */ |
| 170686 | 221, /* (238) eidlist_opt ::= */ |
| 170687 | 221, /* (239) eidlist_opt ::= LP eidlist RP */ |
| 170688 | 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ |
| 170689 | 232, /* (241) eidlist ::= nm collate sortorder */ |
| 170690 | 282, /* (242) collate ::= */ |
| 170691 | 282, /* (243) collate ::= COLLATE ID|STRING */ |
| 170692 | 190, /* (244) cmd ::= DROP INDEX ifexists fullname */ |
| 170693 | 190, /* (245) cmd ::= VACUUM vinto */ |
| 170694 | 190, /* (246) cmd ::= VACUUM nm vinto */ |
| 170695 | 283, /* (247) vinto ::= INTO expr */ |
| 170696 | 283, /* (248) vinto ::= */ |
| 170697 | 190, /* (249) cmd ::= PRAGMA nm dbnm */ |
| 170698 | 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 170699 | 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 170700 | 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 170701 | 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 170702 | 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ |
| 170703 | 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ |
| 170704 | 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 170705 | 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 170706 | 287, /* (258) trigger_time ::= BEFORE|AFTER */ |
| 170707 | 287, /* (259) trigger_time ::= INSTEAD OF */ |
| 170708 | 287, /* (260) trigger_time ::= */ |
| 170709 | 288, /* (261) trigger_event ::= DELETE|INSERT */ |
| 170710 | 288, /* (262) trigger_event ::= UPDATE */ |
| 170711 | 288, /* (263) trigger_event ::= UPDATE OF idlist */ |
| 170712 | 290, /* (264) when_clause ::= */ |
| 170713 | 290, /* (265) when_clause ::= WHEN expr */ |
| 170714 | 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 170715 | 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ |
| 170716 | 292, /* (268) trnm ::= nm DOT nm */ |
| 170717 | 293, /* (269) tridxby ::= INDEXED BY nm */ |
| 170718 | 293, /* (270) tridxby ::= NOT INDEXED */ |
| 170719 | 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 170720 | 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 170721 | 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 170722 | 291, /* (274) trigger_cmd ::= scanpt select scanpt */ |
| 170723 | 217, /* (275) expr ::= RAISE LP IGNORE RP */ |
| 170724 | 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ |
| 170725 | 236, /* (277) raisetype ::= ROLLBACK */ |
| 170726 | 236, /* (278) raisetype ::= ABORT */ |
| 170727 | 236, /* (279) raisetype ::= FAIL */ |
| 170728 | 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ |
| 170729 | 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 170730 | 190, /* (282) cmd ::= DETACH database_kw_opt expr */ |
| 170731 | 295, /* (283) key_opt ::= */ |
| 170732 | 295, /* (284) key_opt ::= KEY expr */ |
| 170733 | 190, /* (285) cmd ::= REINDEX */ |
| 170734 | 190, /* (286) cmd ::= REINDEX nm dbnm */ |
| 170735 | 190, /* (287) cmd ::= ANALYZE */ |
| 170736 | 190, /* (288) cmd ::= ANALYZE nm dbnm */ |
| 170737 | 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 170738 | 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 170739 | 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 170740 | 296, /* (292) add_column_fullname ::= fullname */ |
| 170741 | 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 170742 | 190, /* (294) cmd ::= create_vtab */ |
| 170743 | 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */ |
| 170744 | 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 170745 | 300, /* (297) vtabarg ::= */ |
| 170746 | 301, /* (298) vtabargtoken ::= ANY */ |
| 170747 | 301, /* (299) vtabargtoken ::= lp anylist RP */ |
| 170748 | 302, /* (300) lp ::= LP */ |
| 170749 | 266, /* (301) with ::= WITH wqlist */ |
| 170750 | 266, /* (302) with ::= WITH RECURSIVE wqlist */ |
| 170751 | 305, /* (303) wqas ::= AS */ |
| 170752 | 305, /* (304) wqas ::= AS MATERIALIZED */ |
| 170753 | 305, /* (305) wqas ::= AS NOT MATERIALIZED */ |
| 170754 | 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 170755 | 241, /* (307) wqlist ::= wqitem */ |
| 170756 | 241, /* (308) wqlist ::= wqlist COMMA wqitem */ |
| 170757 | 306, /* (309) windowdefn_list ::= windowdefn */ |
| 170758 | 306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 170759 | 307, /* (311) windowdefn ::= nm AS LP window RP */ |
| 170760 | 308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170761 | 308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 170762 | 308, /* (314) window ::= ORDER BY sortlist frame_opt */ |
| 170763 | 308, /* (315) window ::= nm ORDER BY sortlist frame_opt */ |
| 170764 | 308, /* (316) window ::= frame_opt */ |
| 170765 | 308, /* (317) window ::= nm frame_opt */ |
| 170766 | 309, /* (318) frame_opt ::= */ |
| 170767 | 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 170768 | 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 170769 | 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ |
| 170770 | 315, /* (322) frame_bound_s ::= frame_bound */ |
| 170771 | 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 170772 | 316, /* (324) frame_bound_e ::= frame_bound */ |
| 170773 | 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ |
| 170774 | 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 170775 | 314, /* (327) frame_bound ::= CURRENT ROW */ |
| 170776 | 317, /* (328) frame_exclude_opt ::= */ |
| 170777 | 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 170778 | 318, /* (330) frame_exclude ::= NO OTHERS */ |
| 170779 | 318, /* (331) frame_exclude ::= CURRENT ROW */ |
| 170780 | 318, /* (332) frame_exclude ::= GROUP|TIES */ |
| 170781 | 251, /* (333) window_clause ::= WINDOW windowdefn_list */ |
| 170782 | 273, /* (334) filter_over ::= filter_clause over_clause */ |
| 170783 | 273, /* (335) filter_over ::= over_clause */ |
| 170784 | 273, /* (336) filter_over ::= filter_clause */ |
| 170785 | 312, /* (337) over_clause ::= OVER LP window RP */ |
| 170786 | 312, /* (338) over_clause ::= OVER nm */ |
| 170787 | 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ |
| 170788 | 185, /* (340) input ::= cmdlist */ |
| 170789 | 186, /* (341) cmdlist ::= cmdlist ecmd */ |
| 170790 | 186, /* (342) cmdlist ::= ecmd */ |
| 170791 | 187, /* (343) ecmd ::= SEMI */ |
| 170792 | 187, /* (344) ecmd ::= cmdx SEMI */ |
| 170793 | 187, /* (345) ecmd ::= explain cmdx SEMI */ |
| 170794 | 192, /* (346) trans_opt ::= */ |
| 170795 | 192, /* (347) trans_opt ::= TRANSACTION */ |
| 170796 | 192, /* (348) trans_opt ::= TRANSACTION nm */ |
| 170797 | 194, /* (349) savepoint_opt ::= SAVEPOINT */ |
| 170798 | 194, /* (350) savepoint_opt ::= */ |
| 170799 | 190, /* (351) cmd ::= create_table create_table_args */ |
| 170800 | 203, /* (352) table_option_set ::= table_option */ |
| 170801 | 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ |
| 170802 | 201, /* (354) columnlist ::= columnname carglist */ |
| 170803 | 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */ |
| 170804 | 193, /* (356) nm ::= STRING */ |
| 170805 | 208, /* (357) typetoken ::= typename */ |
| 170806 | 209, /* (358) typename ::= ID|STRING */ |
| 170807 | 210, /* (359) signed ::= plus_num */ |
| 170808 | 210, /* (360) signed ::= minus_num */ |
| 170809 | 207, /* (361) carglist ::= carglist ccons */ |
| 170810 | 207, /* (362) carglist ::= */ |
| 170811 | 215, /* (363) ccons ::= NULL onconf */ |
| 170812 | 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */ |
| 170813 | 215, /* (365) ccons ::= AS generated */ |
| 170814 | 202, /* (366) conslist_opt ::= COMMA conslist */ |
| 170815 | 228, /* (367) conslist ::= conslist tconscomma tcons */ |
| 170816 | 228, /* (368) conslist ::= tcons */ |
| 170817 | 229, /* (369) tconscomma ::= */ |
| 170818 | 233, /* (370) defer_subclause_opt ::= defer_subclause */ |
| 170819 | 235, /* (371) resolvetype ::= raisetype */ |
| 170820 | 239, /* (372) selectnowith ::= oneselect */ |
| 170821 | 240, /* (373) oneselect ::= values */ |
| 170822 | 254, /* (374) sclp ::= selcollist COMMA */ |
| 170823 | 255, /* (375) as ::= ID|STRING */ |
| 170824 | 264, /* (376) indexed_opt ::= indexed_by */ |
| 170825 | 272, /* (377) returning ::= */ |
| 170826 | 217, /* (378) expr ::= term */ |
| 170827 | 274, /* (379) likeop ::= LIKE_KW|MATCH */ |
| 170828 | 278, /* (380) case_operand ::= expr */ |
| 170829 | 261, /* (381) exprlist ::= nexprlist */ |
| 170830 | 284, /* (382) nmnum ::= plus_num */ |
| 170831 | 284, /* (383) nmnum ::= nm */ |
| 170832 | 284, /* (384) nmnum ::= ON */ |
| 170833 | 284, /* (385) nmnum ::= DELETE */ |
| @@ -170754,162 +171080,162 @@ | |
| 171080 | -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ |
| 171081 | -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ |
| 171082 | -4, /* (226) case_exprlist ::= WHEN expr THEN expr */ |
| 171083 | -2, /* (227) case_else ::= ELSE expr */ |
| 171084 | 0, /* (228) case_else ::= */ |
| 171085 | 0, /* (229) case_operand ::= */ |
| 171086 | 0, /* (230) exprlist ::= */ |
| 171087 | -3, /* (231) nexprlist ::= nexprlist COMMA expr */ |
| 171088 | -1, /* (232) nexprlist ::= expr */ |
| 171089 | 0, /* (233) paren_exprlist ::= */ |
| 171090 | -3, /* (234) paren_exprlist ::= LP exprlist RP */ |
| 171091 | -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 171092 | -1, /* (236) uniqueflag ::= UNIQUE */ |
| 171093 | 0, /* (237) uniqueflag ::= */ |
| 171094 | 0, /* (238) eidlist_opt ::= */ |
| 171095 | -3, /* (239) eidlist_opt ::= LP eidlist RP */ |
| 171096 | -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ |
| 171097 | -3, /* (241) eidlist ::= nm collate sortorder */ |
| 171098 | 0, /* (242) collate ::= */ |
| 171099 | -2, /* (243) collate ::= COLLATE ID|STRING */ |
| 171100 | -4, /* (244) cmd ::= DROP INDEX ifexists fullname */ |
| 171101 | -2, /* (245) cmd ::= VACUUM vinto */ |
| 171102 | -3, /* (246) cmd ::= VACUUM nm vinto */ |
| 171103 | -2, /* (247) vinto ::= INTO expr */ |
| 171104 | 0, /* (248) vinto ::= */ |
| 171105 | -3, /* (249) cmd ::= PRAGMA nm dbnm */ |
| 171106 | -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 171107 | -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 171108 | -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 171109 | -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 171110 | -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ |
| 171111 | -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ |
| 171112 | -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 171113 | -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 171114 | -1, /* (258) trigger_time ::= BEFORE|AFTER */ |
| 171115 | -2, /* (259) trigger_time ::= INSTEAD OF */ |
| 171116 | 0, /* (260) trigger_time ::= */ |
| 171117 | -1, /* (261) trigger_event ::= DELETE|INSERT */ |
| 171118 | -1, /* (262) trigger_event ::= UPDATE */ |
| 171119 | -3, /* (263) trigger_event ::= UPDATE OF idlist */ |
| 171120 | 0, /* (264) when_clause ::= */ |
| 171121 | -2, /* (265) when_clause ::= WHEN expr */ |
| 171122 | -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 171123 | -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ |
| 171124 | -3, /* (268) trnm ::= nm DOT nm */ |
| 171125 | -3, /* (269) tridxby ::= INDEXED BY nm */ |
| 171126 | -2, /* (270) tridxby ::= NOT INDEXED */ |
| 171127 | -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 171128 | -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 171129 | -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 171130 | -3, /* (274) trigger_cmd ::= scanpt select scanpt */ |
| 171131 | -4, /* (275) expr ::= RAISE LP IGNORE RP */ |
| 171132 | -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ |
| 171133 | -1, /* (277) raisetype ::= ROLLBACK */ |
| 171134 | -1, /* (278) raisetype ::= ABORT */ |
| 171135 | -1, /* (279) raisetype ::= FAIL */ |
| 171136 | -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ |
| 171137 | -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 171138 | -3, /* (282) cmd ::= DETACH database_kw_opt expr */ |
| 171139 | 0, /* (283) key_opt ::= */ |
| 171140 | -2, /* (284) key_opt ::= KEY expr */ |
| 171141 | -1, /* (285) cmd ::= REINDEX */ |
| 171142 | -3, /* (286) cmd ::= REINDEX nm dbnm */ |
| 171143 | -1, /* (287) cmd ::= ANALYZE */ |
| 171144 | -3, /* (288) cmd ::= ANALYZE nm dbnm */ |
| 171145 | -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 171146 | -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 171147 | -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 171148 | -1, /* (292) add_column_fullname ::= fullname */ |
| 171149 | -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 171150 | -1, /* (294) cmd ::= create_vtab */ |
| 171151 | -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */ |
| 171152 | -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 171153 | 0, /* (297) vtabarg ::= */ |
| 171154 | -1, /* (298) vtabargtoken ::= ANY */ |
| 171155 | -3, /* (299) vtabargtoken ::= lp anylist RP */ |
| 171156 | -1, /* (300) lp ::= LP */ |
| 171157 | -2, /* (301) with ::= WITH wqlist */ |
| 171158 | -3, /* (302) with ::= WITH RECURSIVE wqlist */ |
| 171159 | -1, /* (303) wqas ::= AS */ |
| 171160 | -2, /* (304) wqas ::= AS MATERIALIZED */ |
| 171161 | -3, /* (305) wqas ::= AS NOT MATERIALIZED */ |
| 171162 | -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 171163 | -1, /* (307) wqlist ::= wqitem */ |
| 171164 | -3, /* (308) wqlist ::= wqlist COMMA wqitem */ |
| 171165 | -1, /* (309) windowdefn_list ::= windowdefn */ |
| 171166 | -3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 171167 | -5, /* (311) windowdefn ::= nm AS LP window RP */ |
| 171168 | -5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 171169 | -6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 171170 | -4, /* (314) window ::= ORDER BY sortlist frame_opt */ |
| 171171 | -5, /* (315) window ::= nm ORDER BY sortlist frame_opt */ |
| 171172 | -1, /* (316) window ::= frame_opt */ |
| 171173 | -2, /* (317) window ::= nm frame_opt */ |
| 171174 | 0, /* (318) frame_opt ::= */ |
| 171175 | -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 171176 | -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 171177 | -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ |
| 171178 | -1, /* (322) frame_bound_s ::= frame_bound */ |
| 171179 | -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 171180 | -1, /* (324) frame_bound_e ::= frame_bound */ |
| 171181 | -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ |
| 171182 | -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 171183 | -2, /* (327) frame_bound ::= CURRENT ROW */ |
| 171184 | 0, /* (328) frame_exclude_opt ::= */ |
| 171185 | -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 171186 | -2, /* (330) frame_exclude ::= NO OTHERS */ |
| 171187 | -2, /* (331) frame_exclude ::= CURRENT ROW */ |
| 171188 | -1, /* (332) frame_exclude ::= GROUP|TIES */ |
| 171189 | -2, /* (333) window_clause ::= WINDOW windowdefn_list */ |
| 171190 | -2, /* (334) filter_over ::= filter_clause over_clause */ |
| 171191 | -1, /* (335) filter_over ::= over_clause */ |
| 171192 | -1, /* (336) filter_over ::= filter_clause */ |
| 171193 | -4, /* (337) over_clause ::= OVER LP window RP */ |
| 171194 | -2, /* (338) over_clause ::= OVER nm */ |
| 171195 | -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ |
| 171196 | -1, /* (340) input ::= cmdlist */ |
| 171197 | -2, /* (341) cmdlist ::= cmdlist ecmd */ |
| 171198 | -1, /* (342) cmdlist ::= ecmd */ |
| 171199 | -1, /* (343) ecmd ::= SEMI */ |
| 171200 | -2, /* (344) ecmd ::= cmdx SEMI */ |
| 171201 | -3, /* (345) ecmd ::= explain cmdx SEMI */ |
| 171202 | 0, /* (346) trans_opt ::= */ |
| 171203 | -1, /* (347) trans_opt ::= TRANSACTION */ |
| 171204 | -2, /* (348) trans_opt ::= TRANSACTION nm */ |
| 171205 | -1, /* (349) savepoint_opt ::= SAVEPOINT */ |
| 171206 | 0, /* (350) savepoint_opt ::= */ |
| 171207 | -2, /* (351) cmd ::= create_table create_table_args */ |
| 171208 | -1, /* (352) table_option_set ::= table_option */ |
| 171209 | -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ |
| 171210 | -2, /* (354) columnlist ::= columnname carglist */ |
| 171211 | -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */ |
| 171212 | -1, /* (356) nm ::= STRING */ |
| 171213 | -1, /* (357) typetoken ::= typename */ |
| 171214 | -1, /* (358) typename ::= ID|STRING */ |
| 171215 | -1, /* (359) signed ::= plus_num */ |
| 171216 | -1, /* (360) signed ::= minus_num */ |
| 171217 | -2, /* (361) carglist ::= carglist ccons */ |
| 171218 | 0, /* (362) carglist ::= */ |
| 171219 | -2, /* (363) ccons ::= NULL onconf */ |
| 171220 | -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */ |
| 171221 | -2, /* (365) ccons ::= AS generated */ |
| 171222 | -2, /* (366) conslist_opt ::= COMMA conslist */ |
| 171223 | -3, /* (367) conslist ::= conslist tconscomma tcons */ |
| 171224 | -1, /* (368) conslist ::= tcons */ |
| 171225 | 0, /* (369) tconscomma ::= */ |
| 171226 | -1, /* (370) defer_subclause_opt ::= defer_subclause */ |
| 171227 | -1, /* (371) resolvetype ::= raisetype */ |
| 171228 | -1, /* (372) selectnowith ::= oneselect */ |
| 171229 | -1, /* (373) oneselect ::= values */ |
| 171230 | -2, /* (374) sclp ::= selcollist COMMA */ |
| 171231 | -1, /* (375) as ::= ID|STRING */ |
| 171232 | -1, /* (376) indexed_opt ::= indexed_by */ |
| 171233 | 0, /* (377) returning ::= */ |
| 171234 | -1, /* (378) expr ::= term */ |
| 171235 | -1, /* (379) likeop ::= LIKE_KW|MATCH */ |
| 171236 | -1, /* (380) case_operand ::= expr */ |
| 171237 | -1, /* (381) exprlist ::= nexprlist */ |
| 171238 | -1, /* (382) nmnum ::= plus_num */ |
| 171239 | -1, /* (383) nmnum ::= nm */ |
| 171240 | -1, /* (384) nmnum ::= ON */ |
| 171241 | -1, /* (385) nmnum ::= DELETE */ |
| @@ -170987,11 +171313,11 @@ | |
| 171313 | {yymsp[1].minor.yy394 = TK_DEFERRED;} |
| 171314 | break; |
| 171315 | case 5: /* transtype ::= DEFERRED */ |
| 171316 | case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); |
| 171317 | case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); |
| 171318 | case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); |
| 171319 | {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} |
| 171320 | break; |
| 171321 | case 8: /* cmd ::= COMMIT|END trans_opt */ |
| 171322 | case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); |
| 171323 | {sqlite3EndTransaction(pParse,yymsp[-1].major);} |
| @@ -171024,11 +171350,11 @@ | |
| 171350 | case 47: /* autoinc ::= */ yytestcase(yyruleno==47); |
| 171351 | case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); |
| 171352 | case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); |
| 171353 | case 81: /* ifexists ::= */ yytestcase(yyruleno==81); |
| 171354 | case 98: /* distinct ::= */ yytestcase(yyruleno==98); |
| 171355 | case 242: /* collate ::= */ yytestcase(yyruleno==242); |
| 171356 | {yymsp[1].minor.yy394 = 0;} |
| 171357 | break; |
| 171358 | case 16: /* ifnotexists ::= IF NOT EXISTS */ |
| 171359 | {yymsp[-2].minor.yy394 = 1;} |
| 171360 | break; |
| @@ -171210,11 +171536,11 @@ | |
| 171536 | break; |
| 171537 | case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ |
| 171538 | case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); |
| 171539 | case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215); |
| 171540 | case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218); |
| 171541 | case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243); |
| 171542 | {yymsp[-1].minor.yy394 = 1;} |
| 171543 | break; |
| 171544 | case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ |
| 171545 | {yymsp[-1].minor.yy394 = 0;} |
| 171546 | break; |
| @@ -171360,13 +171686,13 @@ | |
| 171686 | {yymsp[0].minor.yy394 = SF_All;} |
| 171687 | break; |
| 171688 | case 99: /* sclp ::= */ |
| 171689 | case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); |
| 171690 | case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); |
| 171691 | case 230: /* exprlist ::= */ yytestcase(yyruleno==230); |
| 171692 | case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233); |
| 171693 | case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238); |
| 171694 | {yymsp[1].minor.yy322 = 0;} |
| 171695 | break; |
| 171696 | case 100: /* selcollist ::= sclp scanpt expr scanpt as */ |
| 171697 | { |
| 171698 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); |
| @@ -171388,12 +171714,12 @@ | |
| 171714 | yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); |
| 171715 | } |
| 171716 | break; |
| 171717 | case 103: /* as ::= AS nm */ |
| 171718 | case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); |
| 171719 | case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254); |
| 171720 | case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255); |
| 171721 | {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} |
| 171722 | break; |
| 171723 | case 105: /* from ::= */ |
| 171724 | case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); |
| 171725 | {yymsp[1].minor.yy131 = 0;} |
| @@ -171433,11 +171759,11 @@ | |
| 171759 | break; |
| 171760 | case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ |
| 171761 | { |
| 171762 | if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ |
| 171763 | yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; |
| 171764 | }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){ |
| 171765 | yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); |
| 171766 | if( yymsp[-5].minor.yy131 ){ |
| 171767 | SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; |
| 171768 | SrcItem *pOld = yymsp[-3].minor.yy131->a; |
| 171769 | pNew->zName = pOld->zName; |
| @@ -171562,19 +171888,19 @@ | |
| 171888 | case 144: /* having_opt ::= */ |
| 171889 | case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); |
| 171890 | case 151: /* where_opt ::= */ yytestcase(yyruleno==151); |
| 171891 | case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); |
| 171892 | case 228: /* case_else ::= */ yytestcase(yyruleno==228); |
| 171893 | case 229: /* case_operand ::= */ yytestcase(yyruleno==229); |
| 171894 | case 248: /* vinto ::= */ yytestcase(yyruleno==248); |
| 171895 | {yymsp[1].minor.yy528 = 0;} |
| 171896 | break; |
| 171897 | case 145: /* having_opt ::= HAVING expr */ |
| 171898 | case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); |
| 171899 | case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); |
| 171900 | case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227); |
| 171901 | case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247); |
| 171902 | {yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} |
| 171903 | break; |
| 171904 | case 147: /* limit_opt ::= LIMIT expr */ |
| 171905 | {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} |
| 171906 | break; |
| @@ -172001,394 +172327,391 @@ | |
| 172327 | { |
| 172328 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); |
| 172329 | yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); |
| 172330 | } |
| 172331 | break; |
| 172332 | case 231: /* nexprlist ::= nexprlist COMMA expr */ |
| 172333 | {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} |
| 172334 | break; |
| 172335 | case 232: /* nexprlist ::= expr */ |
| 172336 | {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} |
| 172337 | break; |
| 172338 | case 234: /* paren_exprlist ::= LP exprlist RP */ |
| 172339 | case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239); |
| 172340 | {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} |
| 172341 | break; |
| 172342 | case 235: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ |
| 172343 | { |
| 172344 | sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, |
| 172345 | sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, |
| 172346 | &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); |
| 172347 | if( IN_RENAME_OBJECT && pParse->pNewIndex ){ |
| 172348 | sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); |
| 172349 | } |
| 172350 | } |
| 172351 | break; |
| 172352 | case 236: /* uniqueflag ::= UNIQUE */ |
| 172353 | case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278); |
| 172354 | {yymsp[0].minor.yy394 = OE_Abort;} |
| 172355 | break; |
| 172356 | case 237: /* uniqueflag ::= */ |
| 172357 | {yymsp[1].minor.yy394 = OE_None;} |
| 172358 | break; |
| 172359 | case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */ |
| 172360 | { |
| 172361 | yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); |
| 172362 | } |
| 172363 | break; |
| 172364 | case 241: /* eidlist ::= nm collate sortorder */ |
| 172365 | { |
| 172366 | yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ |
| 172367 | } |
| 172368 | break; |
| 172369 | case 244: /* cmd ::= DROP INDEX ifexists fullname */ |
| 172370 | {sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} |
| 172371 | break; |
| 172372 | case 245: /* cmd ::= VACUUM vinto */ |
| 172373 | {sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} |
| 172374 | break; |
| 172375 | case 246: /* cmd ::= VACUUM nm vinto */ |
| 172376 | {sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} |
| 172377 | break; |
| 172378 | case 249: /* cmd ::= PRAGMA nm dbnm */ |
| 172379 | {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} |
| 172380 | break; |
| 172381 | case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ |
| 172382 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} |
| 172383 | break; |
| 172384 | case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ |
| 172385 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} |
| 172386 | break; |
| 172387 | case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ |
| 172388 | {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} |
| 172389 | break; |
| 172390 | case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ |
| 172391 | {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} |
| 172392 | break; |
| 172393 | case 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ |
| 172394 | { |
| 172395 | Token all; |
| 172396 | all.z = yymsp[-3].minor.yy0.z; |
| 172397 | all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; |
| 172398 | sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); |
| 172399 | } |
| 172400 | break; |
| 172401 | case 257: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ |
| 172402 | { |
| 172403 | sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); |
| 172404 | yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ |
| 172405 | } |
| 172406 | break; |
| 172407 | case 258: /* trigger_time ::= BEFORE|AFTER */ |
| 172408 | { yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } |
| 172409 | break; |
| 172410 | case 259: /* trigger_time ::= INSTEAD OF */ |
| 172411 | { yymsp[-1].minor.yy394 = TK_INSTEAD;} |
| 172412 | break; |
| 172413 | case 260: /* trigger_time ::= */ |
| 172414 | { yymsp[1].minor.yy394 = TK_BEFORE; } |
| 172415 | break; |
| 172416 | case 261: /* trigger_event ::= DELETE|INSERT */ |
| 172417 | case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262); |
| 172418 | {yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} |
| 172419 | break; |
| 172420 | case 263: /* trigger_event ::= UPDATE OF idlist */ |
| 172421 | {yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} |
| 172422 | break; |
| 172423 | case 264: /* when_clause ::= */ |
| 172424 | case 283: /* key_opt ::= */ yytestcase(yyruleno==283); |
| 172425 | { yymsp[1].minor.yy528 = 0; } |
| 172426 | break; |
| 172427 | case 265: /* when_clause ::= WHEN expr */ |
| 172428 | case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284); |
| 172429 | { yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } |
| 172430 | break; |
| 172431 | case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ |
| 172432 | { |
| 172433 | assert( yymsp[-2].minor.yy33!=0 ); |
| 172434 | yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; |
| 172435 | yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172436 | } |
| 172437 | break; |
| 172438 | case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */ |
| 172439 | { |
| 172440 | assert( yymsp[-1].minor.yy33!=0 ); |
| 172441 | yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; |
| 172442 | } |
| 172443 | break; |
| 172444 | case 268: /* trnm ::= nm DOT nm */ |
| 172445 | { |
| 172446 | yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; |
| 172447 | sqlite3ErrorMsg(pParse, |
| 172448 | "qualified table names are not allowed on INSERT, UPDATE, and DELETE " |
| 172449 | "statements within triggers"); |
| 172450 | } |
| 172451 | break; |
| 172452 | case 269: /* tridxby ::= INDEXED BY nm */ |
| 172453 | { |
| 172454 | sqlite3ErrorMsg(pParse, |
| 172455 | "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " |
| 172456 | "within triggers"); |
| 172457 | } |
| 172458 | break; |
| 172459 | case 270: /* tridxby ::= NOT INDEXED */ |
| 172460 | { |
| 172461 | sqlite3ErrorMsg(pParse, |
| 172462 | "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " |
| 172463 | "within triggers"); |
| 172464 | } |
| 172465 | break; |
| 172466 | case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ |
| 172467 | {yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172468 | yymsp[-8].minor.yy33 = yylhsminor.yy33; |
| 172469 | break; |
| 172470 | case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ |
| 172471 | { |
| 172472 | yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ |
| 172473 | } |
| 172474 | yymsp[-7].minor.yy33 = yylhsminor.yy33; |
| 172475 | break; |
| 172476 | case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ |
| 172477 | {yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} |
| 172478 | yymsp[-5].minor.yy33 = yylhsminor.yy33; |
| 172479 | break; |
| 172480 | case 274: /* trigger_cmd ::= scanpt select scanpt */ |
| 172481 | {yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} |
| 172482 | yymsp[-2].minor.yy33 = yylhsminor.yy33; |
| 172483 | break; |
| 172484 | case 275: /* expr ::= RAISE LP IGNORE RP */ |
| 172485 | { |
| 172486 | yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); |
| 172487 | if( yymsp[-3].minor.yy528 ){ |
| 172488 | yymsp[-3].minor.yy528->affExpr = OE_Ignore; |
| 172489 | } |
| 172490 | } |
| 172491 | break; |
| 172492 | case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */ |
| 172493 | { |
| 172494 | yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); |
| 172495 | if( yymsp[-5].minor.yy528 ) { |
| 172496 | yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; |
| 172497 | } |
| 172498 | } |
| 172499 | break; |
| 172500 | case 277: /* raisetype ::= ROLLBACK */ |
| 172501 | {yymsp[0].minor.yy394 = OE_Rollback;} |
| 172502 | break; |
| 172503 | case 279: /* raisetype ::= FAIL */ |
| 172504 | {yymsp[0].minor.yy394 = OE_Fail;} |
| 172505 | break; |
| 172506 | case 280: /* cmd ::= DROP TRIGGER ifexists fullname */ |
| 172507 | { |
| 172508 | sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); |
| 172509 | } |
| 172510 | break; |
| 172511 | case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ |
| 172512 | { |
| 172513 | sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); |
| 172514 | } |
| 172515 | break; |
| 172516 | case 282: /* cmd ::= DETACH database_kw_opt expr */ |
| 172517 | { |
| 172518 | sqlite3Detach(pParse, yymsp[0].minor.yy528); |
| 172519 | } |
| 172520 | break; |
| 172521 | case 285: /* cmd ::= REINDEX */ |
| 172522 | {sqlite3Reindex(pParse, 0, 0);} |
| 172523 | break; |
| 172524 | case 286: /* cmd ::= REINDEX nm dbnm */ |
| 172525 | {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172526 | break; |
| 172527 | case 287: /* cmd ::= ANALYZE */ |
| 172528 | {sqlite3Analyze(pParse, 0, 0);} |
| 172529 | break; |
| 172530 | case 288: /* cmd ::= ANALYZE nm dbnm */ |
| 172531 | {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} |
| 172532 | break; |
| 172533 | case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ |
| 172534 | { |
| 172535 | sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); |
| 172536 | } |
| 172537 | break; |
| 172538 | case 290: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ |
| 172539 | { |
| 172540 | yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; |
| 172541 | sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); |
| 172542 | } |
| 172543 | break; |
| 172544 | case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ |
| 172545 | { |
| 172546 | sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); |
| 172547 | } |
| 172548 | break; |
| 172549 | case 292: /* add_column_fullname ::= fullname */ |
| 172550 | { |
| 172551 | disableLookaside(pParse); |
| 172552 | sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); |
| 172553 | } |
| 172554 | break; |
| 172555 | case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ |
| 172556 | { |
| 172557 | sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); |
| 172558 | } |
| 172559 | break; |
| 172560 | case 294: /* cmd ::= create_vtab */ |
| 172561 | {sqlite3VtabFinishParse(pParse,0);} |
| 172562 | break; |
| 172563 | case 295: /* cmd ::= create_vtab LP vtabarglist RP */ |
| 172564 | {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} |
| 172565 | break; |
| 172566 | case 296: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ |
| 172567 | { |
| 172568 | sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); |
| 172569 | } |
| 172570 | break; |
| 172571 | case 297: /* vtabarg ::= */ |
| 172572 | {sqlite3VtabArgInit(pParse);} |
| 172573 | break; |
| 172574 | case 298: /* vtabargtoken ::= ANY */ |
| 172575 | case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299); |
| 172576 | case 300: /* lp ::= LP */ yytestcase(yyruleno==300); |
| 172577 | {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} |
| 172578 | break; |
| 172579 | case 301: /* with ::= WITH wqlist */ |
| 172580 | case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302); |
| 172581 | { sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } |
| 172582 | break; |
| 172583 | case 303: /* wqas ::= AS */ |
| 172584 | {yymsp[0].minor.yy516 = M10d_Any;} |
| 172585 | break; |
| 172586 | case 304: /* wqas ::= AS MATERIALIZED */ |
| 172587 | {yymsp[-1].minor.yy516 = M10d_Yes;} |
| 172588 | break; |
| 172589 | case 305: /* wqas ::= AS NOT MATERIALIZED */ |
| 172590 | {yymsp[-2].minor.yy516 = M10d_No;} |
| 172591 | break; |
| 172592 | case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */ |
| 172593 | { |
| 172594 | yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ |
| 172595 | } |
| 172596 | break; |
| 172597 | case 307: /* wqlist ::= wqitem */ |
| 172598 | { |
| 172599 | yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ |
| 172600 | } |
| 172601 | break; |
| 172602 | case 308: /* wqlist ::= wqlist COMMA wqitem */ |
| 172603 | { |
| 172604 | yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); |
| 172605 | } |
| 172606 | break; |
| 172607 | case 309: /* windowdefn_list ::= windowdefn */ |
| 172608 | { yylhsminor.yy41 = yymsp[0].minor.yy41; } |
| 172609 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172610 | break; |
| 172611 | case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ |
| 172612 | { |
| 172613 | assert( yymsp[0].minor.yy41!=0 ); |
| 172614 | sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); |
| 172615 | yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; |
| 172616 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172617 | } |
| 172618 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172619 | break; |
| 172620 | case 311: /* windowdefn ::= nm AS LP window RP */ |
| 172621 | { |
| 172622 | if( ALWAYS(yymsp[-1].minor.yy41) ){ |
| 172623 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); |
| 172624 | } |
| 172625 | yylhsminor.yy41 = yymsp[-1].minor.yy41; |
| 172626 | } |
| 172627 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172628 | break; |
| 172629 | case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ |
| 172630 | { |
| 172631 | yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); |
| 172632 | } |
| 172633 | break; |
| 172634 | case 313: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ |
| 172635 | { |
| 172636 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); |
| 172637 | } |
| 172638 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172639 | break; |
| 172640 | case 314: /* window ::= ORDER BY sortlist frame_opt */ |
| 172641 | { |
| 172642 | yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); |
| 172643 | } |
| 172644 | break; |
| 172645 | case 315: /* window ::= nm ORDER BY sortlist frame_opt */ |
| 172646 | { |
| 172647 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); |
| 172648 | } |
| 172649 | yymsp[-4].minor.yy41 = yylhsminor.yy41; |
| 172650 | break; |
| 172651 | case 316: /* window ::= frame_opt */ |
| 172652 | case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335); |
| 172653 | { |
| 172654 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172655 | } |
| 172656 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172657 | break; |
| 172658 | case 317: /* window ::= nm frame_opt */ |
| 172659 | { |
| 172660 | yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); |
| 172661 | } |
| 172662 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172663 | break; |
| 172664 | case 318: /* frame_opt ::= */ |
| 172665 | { |
| 172666 | yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); |
| 172667 | } |
| 172668 | break; |
| 172669 | case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ |
| 172670 | { |
| 172671 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); |
| 172672 | } |
| 172673 | yymsp[-2].minor.yy41 = yylhsminor.yy41; |
| 172674 | break; |
| 172675 | case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ |
| 172676 | { |
| 172677 | yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); |
| 172678 | } |
| 172679 | yymsp[-5].minor.yy41 = yylhsminor.yy41; |
| 172680 | break; |
| 172681 | case 322: /* frame_bound_s ::= frame_bound */ |
| 172682 | case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); |
| 172683 | {yylhsminor.yy595 = yymsp[0].minor.yy595;} |
| 172684 | yymsp[0].minor.yy595 = yylhsminor.yy595; |
| 172685 | break; |
| 172686 | case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ |
| 172687 | case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); |
| 172688 | case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); |
| 172689 | {yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} |
| 172690 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172691 | break; |
| 172692 | case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ |
| 172693 | {yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} |
| 172694 | yymsp[-1].minor.yy595 = yylhsminor.yy595; |
| 172695 | break; |
| 172696 | case 328: /* frame_exclude_opt ::= */ |
| 172697 | {yymsp[1].minor.yy516 = 0;} |
| 172698 | break; |
| 172699 | case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ |
| 172700 | {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} |
| 172701 | break; |
| 172702 | case 330: /* frame_exclude ::= NO OTHERS */ |
| 172703 | case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); |
| 172704 | {yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} |
| 172705 | break; |
| 172706 | case 332: /* frame_exclude ::= GROUP|TIES */ |
| 172707 | {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} |
| 172708 | break; |
| 172709 | case 333: /* window_clause ::= WINDOW windowdefn_list */ |
| 172710 | { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } |
| 172711 | break; |
| 172712 | case 334: /* filter_over ::= filter_clause over_clause */ |
| 172713 | { |
| 172714 | if( yymsp[0].minor.yy41 ){ |
| 172715 | yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; |
| 172716 | }else{ |
| 172717 | sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); |
| @@ -172395,11 +172718,11 @@ | |
| 172718 | } |
| 172719 | yylhsminor.yy41 = yymsp[0].minor.yy41; |
| 172720 | } |
| 172721 | yymsp[-1].minor.yy41 = yylhsminor.yy41; |
| 172722 | break; |
| 172723 | case 336: /* filter_over ::= filter_clause */ |
| 172724 | { |
| 172725 | yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172726 | if( yylhsminor.yy41 ){ |
| 172727 | yylhsminor.yy41->eFrmType = TK_FILTER; |
| 172728 | yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; |
| @@ -172407,68 +172730,69 @@ | |
| 172730 | sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); |
| 172731 | } |
| 172732 | } |
| 172733 | yymsp[0].minor.yy41 = yylhsminor.yy41; |
| 172734 | break; |
| 172735 | case 337: /* over_clause ::= OVER LP window RP */ |
| 172736 | { |
| 172737 | yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; |
| 172738 | assert( yymsp[-3].minor.yy41!=0 ); |
| 172739 | } |
| 172740 | break; |
| 172741 | case 338: /* over_clause ::= OVER nm */ |
| 172742 | { |
| 172743 | yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); |
| 172744 | if( yymsp[-1].minor.yy41 ){ |
| 172745 | yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); |
| 172746 | } |
| 172747 | } |
| 172748 | break; |
| 172749 | case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ |
| 172750 | { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } |
| 172751 | break; |
| 172752 | default: |
| 172753 | /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); |
| 172754 | /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); |
| 172755 | /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); |
| 172756 | /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); |
| 172757 | /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); |
| 172758 | /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); |
| 172759 | /* (346) trans_opt ::= */ yytestcase(yyruleno==346); |
| 172760 | /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); |
| 172761 | /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); |
| 172762 | /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); |
| 172763 | /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); |
| 172764 | /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); |
| 172765 | /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); |
| 172766 | /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); |
| 172767 | /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); |
| 172768 | /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355); |
| 172769 | /* (356) nm ::= STRING */ yytestcase(yyruleno==356); |
| 172770 | /* (357) typetoken ::= typename */ yytestcase(yyruleno==357); |
| 172771 | /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358); |
| 172772 | /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359); |
| 172773 | /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); |
| 172774 | /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361); |
| 172775 | /* (362) carglist ::= */ yytestcase(yyruleno==362); |
| 172776 | /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363); |
| 172777 | /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364); |
| 172778 | /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365); |
| 172779 | /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366); |
| 172780 | /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367); |
| 172781 | /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368); |
| 172782 | /* (369) tconscomma ::= */ yytestcase(yyruleno==369); |
| 172783 | /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370); |
| 172784 | /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371); |
| 172785 | /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372); |
| 172786 | /* (373) oneselect ::= values */ yytestcase(yyruleno==373); |
| 172787 | /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374); |
| 172788 | /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375); |
| 172789 | /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376); |
| 172790 | /* (377) returning ::= */ yytestcase(yyruleno==377); |
| 172791 | /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); |
| 172792 | /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); |
| 172793 | /* (380) case_operand ::= expr */ yytestcase(yyruleno==380); |
| 172794 | /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); |
| 172795 | /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); |
| 172796 | /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); |
| 172797 | /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); |
| 172798 | /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); |
| @@ -178496,11 +178820,11 @@ | |
| 178820 | if( pVfs==0 ) return 0; |
| 178821 | |
| 178822 | /* This function works in milliseconds, but the underlying OsSleep() |
| 178823 | ** API uses microseconds. Hence the 1000's. |
| 178824 | */ |
| 178825 | rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000); |
| 178826 | return rc; |
| 178827 | } |
| 178828 | |
| 178829 | /* |
| 178830 | ** Enable or disable the extended result codes. |
| @@ -199500,10 +199824,11 @@ | |
| 199824 | #define JNODE_REMOVE 0x04 /* Do not output */ |
| 199825 | #define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */ |
| 199826 | #define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */ |
| 199827 | #define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */ |
| 199828 | #define JNODE_LABEL 0x40 /* Is a label of an object */ |
| 199829 | #define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */ |
| 199830 | |
| 199831 | |
| 199832 | /* A single node of parsed JSON |
| 199833 | */ |
| 199834 | struct JsonNode { |
| @@ -199526,25 +199851,27 @@ | |
| 199851 | u32 nNode; /* Number of slots of aNode[] used */ |
| 199852 | u32 nAlloc; /* Number of slots of aNode[] allocated */ |
| 199853 | JsonNode *aNode; /* Array of nodes containing the parse */ |
| 199854 | const char *zJson; /* Original JSON string */ |
| 199855 | u32 *aUp; /* Index of parent of each node */ |
| 199856 | u16 iDepth; /* Nesting depth */ |
| 199857 | u8 nErr; /* Number of errors seen */ |
| 199858 | u8 oom; /* Set to true if out of memory */ |
| 199859 | u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ |
| 199860 | int nJson; /* Length of the zJson string in bytes */ |
| 199861 | u32 iErr; /* Error location in zJson[] */ |
| 199862 | u32 iHold; /* Replace cache line with the lowest iHold value */ |
| 199863 | }; |
| 199864 | |
| 199865 | /* |
| 199866 | ** Maximum nesting depth of JSON for this implementation. |
| 199867 | ** |
| 199868 | ** This limit is needed to avoid a stack overflow in the recursive |
| 199869 | ** descent parser. A depth of 1000 is far deeper than any sane JSON |
| 199870 | ** should go. Historical note: This limit was 2000 prior to version 3.42.0 |
| 199871 | */ |
| 199872 | #define JSON_MAX_DEPTH 1000 |
| 199873 | |
| 199874 | /************************************************************************** |
| 199875 | ** Utility routines for dealing with JsonString objects |
| 199876 | **************************************************************************/ |
| 199877 | |
| @@ -199689,10 +200016,133 @@ | |
| 200016 | p->zBuf[p->nUsed++] = c; |
| 200017 | } |
| 200018 | p->zBuf[p->nUsed++] = '"'; |
| 200019 | assert( p->nUsed<p->nAlloc ); |
| 200020 | } |
| 200021 | |
| 200022 | /* |
| 200023 | ** The zIn[0..N] string is a JSON5 string literal. Append to p a translation |
| 200024 | ** of the string literal that standard JSON and that omits all JSON5 |
| 200025 | ** features. |
| 200026 | */ |
| 200027 | static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ |
| 200028 | u32 i; |
| 200029 | jsonAppendChar(p, '"'); |
| 200030 | zIn++; |
| 200031 | N -= 2; |
| 200032 | while( N>0 ){ |
| 200033 | for(i=0; i<N && zIn[i]!='\\'; i++){} |
| 200034 | if( i>0 ){ |
| 200035 | jsonAppendRaw(p, zIn, i); |
| 200036 | zIn += i; |
| 200037 | N -= i; |
| 200038 | if( N==0 ) break; |
| 200039 | } |
| 200040 | assert( zIn[0]=='\\' ); |
| 200041 | switch( (u8)zIn[1] ){ |
| 200042 | case '\'': |
| 200043 | jsonAppendChar(p, '\''); |
| 200044 | break; |
| 200045 | case 'v': |
| 200046 | jsonAppendRaw(p, "\\u0009", 6); |
| 200047 | break; |
| 200048 | case 'x': |
| 200049 | jsonAppendRaw(p, "\\u00", 4); |
| 200050 | jsonAppendRaw(p, &zIn[2], 2); |
| 200051 | zIn += 2; |
| 200052 | N -= 2; |
| 200053 | break; |
| 200054 | case '0': |
| 200055 | jsonAppendRaw(p, "\\u0000", 6); |
| 200056 | break; |
| 200057 | case '\r': |
| 200058 | if( zIn[2]=='\n' ){ |
| 200059 | zIn++; |
| 200060 | N--; |
| 200061 | } |
| 200062 | break; |
| 200063 | case '\n': |
| 200064 | break; |
| 200065 | case 0xe2: |
| 200066 | assert( N>=4 ); |
| 200067 | assert( 0x80==(u8)zIn[2] ); |
| 200068 | assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); |
| 200069 | zIn += 2; |
| 200070 | N -= 2; |
| 200071 | break; |
| 200072 | default: |
| 200073 | jsonAppendRaw(p, zIn, 2); |
| 200074 | break; |
| 200075 | } |
| 200076 | zIn += 2; |
| 200077 | N -= 2; |
| 200078 | } |
| 200079 | jsonAppendChar(p, '"'); |
| 200080 | } |
| 200081 | |
| 200082 | /* |
| 200083 | ** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation |
| 200084 | ** of the string literal that standard JSON and that omits all JSON5 |
| 200085 | ** features. |
| 200086 | */ |
| 200087 | static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ |
| 200088 | if( zIn[0]=='+' ){ |
| 200089 | zIn++; |
| 200090 | N--; |
| 200091 | }else if( zIn[0]=='-' ){ |
| 200092 | jsonAppendChar(p, '-'); |
| 200093 | zIn++; |
| 200094 | N--; |
| 200095 | } |
| 200096 | if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ |
| 200097 | sqlite3_int64 i = 0; |
| 200098 | int rc = sqlite3DecOrHexToI64(zIn, &i); |
| 200099 | if( rc<=1 ){ |
| 200100 | jsonPrintf(100,p,"%lld",i); |
| 200101 | }else{ |
| 200102 | assert( rc==2 ); |
| 200103 | jsonAppendRaw(p, "9.0e999", 7); |
| 200104 | } |
| 200105 | return; |
| 200106 | } |
| 200107 | jsonAppendRaw(p, zIn, N); |
| 200108 | } |
| 200109 | |
| 200110 | /* |
| 200111 | ** The zIn[0..N] string is a JSON5 real literal. Append to p a translation |
| 200112 | ** of the string literal that standard JSON and that omits all JSON5 |
| 200113 | ** features. |
| 200114 | */ |
| 200115 | static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ |
| 200116 | u32 i; |
| 200117 | if( zIn[0]=='+' ){ |
| 200118 | zIn++; |
| 200119 | N--; |
| 200120 | }else if( zIn[0]=='-' ){ |
| 200121 | jsonAppendChar(p, '-'); |
| 200122 | zIn++; |
| 200123 | N--; |
| 200124 | } |
| 200125 | if( zIn[0]=='.' ){ |
| 200126 | jsonAppendChar(p, '0'); |
| 200127 | } |
| 200128 | for(i=0; i<N; i++){ |
| 200129 | if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){ |
| 200130 | i++; |
| 200131 | jsonAppendRaw(p, zIn, i); |
| 200132 | zIn += i; |
| 200133 | N -= i; |
| 200134 | jsonAppendChar(p, '0'); |
| 200135 | break; |
| 200136 | } |
| 200137 | } |
| 200138 | if( N>0 ){ |
| 200139 | jsonAppendRaw(p, zIn, N); |
| 200140 | } |
| 200141 | } |
| 200142 | |
| 200143 | |
| 200144 | |
| 200145 | /* |
| 200146 | ** Append a function parameter value to the JSON string under |
| 200147 | ** construction. |
| 200148 | */ |
| @@ -199820,21 +200270,42 @@ | |
| 200270 | case JSON_FALSE: { |
| 200271 | jsonAppendRaw(pOut, "false", 5); |
| 200272 | break; |
| 200273 | } |
| 200274 | case JSON_STRING: { |
| 200275 | assert( pNode->eU==1 ); |
| 200276 | if( pNode->jnFlags & JNODE_RAW ){ |
| 200277 | if( pNode->jnFlags & JNODE_LABEL ){ |
| 200278 | jsonAppendChar(pOut, '"'); |
| 200279 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 200280 | jsonAppendChar(pOut, '"'); |
| 200281 | }else{ |
| 200282 | jsonAppendString(pOut, pNode->u.zJContent, pNode->n); |
| 200283 | } |
| 200284 | }else if( pNode->jnFlags & JNODE_JSON5 ){ |
| 200285 | jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); |
| 200286 | }else{ |
| 200287 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 200288 | } |
| 200289 | break; |
| 200290 | } |
| 200291 | case JSON_REAL: { |
| 200292 | assert( pNode->eU==1 ); |
| 200293 | if( pNode->jnFlags & JNODE_JSON5 ){ |
| 200294 | jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); |
| 200295 | }else{ |
| 200296 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 200297 | } |
| 200298 | break; |
| 200299 | } |
| 200300 | case JSON_INT: { |
| 200301 | assert( pNode->eU==1 ); |
| 200302 | if( pNode->jnFlags & JNODE_JSON5 ){ |
| 200303 | jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); |
| 200304 | }else{ |
| 200305 | jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); |
| 200306 | } |
| 200307 | break; |
| 200308 | } |
| 200309 | case JSON_ARRAY: { |
| 200310 | u32 j = 1; |
| 200311 | jsonAppendChar(pOut, '['); |
| @@ -199946,63 +200417,45 @@ | |
| 200417 | sqlite3_result_int(pCtx, 0); |
| 200418 | break; |
| 200419 | } |
| 200420 | case JSON_INT: { |
| 200421 | sqlite3_int64 i = 0; |
| 200422 | int rc; |
| 200423 | int bNeg = 0; |
| 200424 | const char *z; |
| 200425 | |
| 200426 | |
| 200427 | assert( pNode->eU==1 ); |
| 200428 | z = pNode->u.zJContent; |
| 200429 | if( z[0]=='-' ){ z++; bNeg = 1; } |
| 200430 | else if( z[0]=='+' ){ z++; } |
| 200431 | rc = sqlite3DecOrHexToI64(z, &i); |
| 200432 | if( rc<=1 ){ |
| 200433 | sqlite3_result_int64(pCtx, bNeg ? -i : i); |
| 200434 | }else if( rc==3 && bNeg ){ |
| 200435 | sqlite3_result_int64(pCtx, SMALLEST_INT64); |
| 200436 | }else{ |
| 200437 | goto to_double; |
| 200438 | } |
| 200439 | break; |
| 200440 | } |
| 200441 | case JSON_REAL: { |
| 200442 | double r; |
| 200443 | const char *z; |
| 200444 | assert( pNode->eU==1 ); |
| 200445 | to_double: |
| 200446 | z = pNode->u.zJContent; |
| 200447 | sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); |
| 200448 | sqlite3_result_double(pCtx, r); |
| 200449 | break; |
| 200450 | } |
| 200451 | case JSON_STRING: { |
| 200452 | if( pNode->jnFlags & JNODE_RAW ){ |
| 200453 | assert( pNode->eU==1 ); |
| 200454 | sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, |
| 200455 | SQLITE_TRANSIENT); |
| 200456 | }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ |
| 200457 | /* JSON formatted without any backslash-escapes */ |
| 200458 | assert( pNode->eU==1 ); |
| 200459 | sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, |
| 200460 | SQLITE_TRANSIENT); |
| 200461 | }else{ |
| @@ -200010,22 +200463,21 @@ | |
| 200463 | u32 i; |
| 200464 | u32 n = pNode->n; |
| 200465 | const char *z; |
| 200466 | char *zOut; |
| 200467 | u32 j; |
| 200468 | u32 nOut = n; |
| 200469 | assert( pNode->eU==1 ); |
| 200470 | z = pNode->u.zJContent; |
| 200471 | zOut = sqlite3_malloc( nOut+1 ); |
| 200472 | if( zOut==0 ){ |
| 200473 | sqlite3_result_error_nomem(pCtx); |
| 200474 | break; |
| 200475 | } |
| 200476 | for(i=1, j=0; i<n-1; i++){ |
| 200477 | char c = z[i]; |
| 200478 | if( c=='\\' ){ |
| 200479 | c = z[++i]; |
| 200480 | if( c=='u' ){ |
| 200481 | u32 v = jsonHexToInt4(z+i+1); |
| 200482 | i += 4; |
| 200483 | if( v==0 ) break; |
| @@ -200053,26 +200505,44 @@ | |
| 200505 | zOut[j++] = 0xe0 | (v>>12); |
| 200506 | zOut[j++] = 0x80 | ((v>>6)&0x3f); |
| 200507 | zOut[j++] = 0x80 | (v&0x3f); |
| 200508 | } |
| 200509 | } |
| 200510 | continue; |
| 200511 | }else if( c=='b' ){ |
| 200512 | c = '\b'; |
| 200513 | }else if( c=='f' ){ |
| 200514 | c = '\f'; |
| 200515 | }else if( c=='n' ){ |
| 200516 | c = '\n'; |
| 200517 | }else if( c=='r' ){ |
| 200518 | c = '\r'; |
| 200519 | }else if( c=='t' ){ |
| 200520 | c = '\t'; |
| 200521 | }else if( c=='v' ){ |
| 200522 | c = '\v'; |
| 200523 | }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ |
| 200524 | /* pass through unchanged */ |
| 200525 | }else if( c=='0' ){ |
| 200526 | c = 0; |
| 200527 | }else if( c=='x' ){ |
| 200528 | c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); |
| 200529 | i += 2; |
| 200530 | }else if( c=='\r' && z[i+1]=='\n' ){ |
| 200531 | i++; |
| 200532 | continue; |
| 200533 | }else if( 0xe2==(u8)c ){ |
| 200534 | assert( 0x80==(u8)z[i+1] ); |
| 200535 | assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); |
| 200536 | i += 2; |
| 200537 | continue; |
| 200538 | }else{ |
| 200539 | continue; |
| 200540 | } |
| 200541 | } /* end if( c=='\\' ) */ |
| 200542 | zOut[j++] = c; |
| 200543 | } /* end for() */ |
| 200544 | zOut[j] = 0; |
| 200545 | sqlite3_result_text(pCtx, zOut, j, sqlite3_free); |
| 200546 | } |
| 200547 | break; |
| 200548 | } |
| @@ -200136,28 +200606,159 @@ | |
| 200606 | JsonNode *p; |
| 200607 | if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){ |
| 200608 | return jsonParseAddNodeExpand(pParse, eType, n, zContent); |
| 200609 | } |
| 200610 | p = &pParse->aNode[pParse->nNode]; |
| 200611 | p->eType = (u8)(eType & 0xff); |
| 200612 | p->jnFlags = (u8)(eType >> 8); |
| 200613 | VVA( p->eU = zContent ? 1 : 0 ); |
| 200614 | p->n = n; |
| 200615 | p->u.zJContent = zContent; |
| 200616 | return pParse->nNode++; |
| 200617 | } |
| 200618 | |
| 200619 | /* |
| 200620 | ** Return true if z[] begins with 2 (or more) hexadecimal digits |
| 200621 | */ |
| 200622 | static int jsonIs2Hex(const char *z){ |
| 200623 | return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]); |
| 200624 | } |
| 200625 | |
| 200626 | /* |
| 200627 | ** Return true if z[] begins with 4 (or more) hexadecimal digits |
| 200628 | */ |
| 200629 | static int jsonIs4Hex(const char *z){ |
| 200630 | return jsonIs2Hex(z) && jsonIs2Hex(&z[2]); |
| 200631 | } |
| 200632 | |
| 200633 | /* |
| 200634 | ** Return the number of bytes of JSON5 whitespace at the beginning of |
| 200635 | ** the input string z[]. |
| 200636 | ** |
| 200637 | ** JSON5 whitespace consists of any of the following characters: |
| 200638 | ** |
| 200639 | ** Unicode UTF-8 Name |
| 200640 | ** U+0009 09 horizontal tab |
| 200641 | ** U+000a 0a line feed |
| 200642 | ** U+000b 0b vertical tab |
| 200643 | ** U+000c 0c form feed |
| 200644 | ** U+000d 0d carriage return |
| 200645 | ** U+0020 20 space |
| 200646 | ** U+00a0 c2 a0 non-breaking space |
| 200647 | ** U+1680 e1 9a 80 ogham space mark |
| 200648 | ** U+2000 e2 80 80 en quad |
| 200649 | ** U+2001 e2 80 81 em quad |
| 200650 | ** U+2002 e2 80 82 en space |
| 200651 | ** U+2003 e2 80 83 em space |
| 200652 | ** U+2004 e2 80 84 three-per-em space |
| 200653 | ** U+2005 e2 80 85 four-per-em space |
| 200654 | ** U+2006 e2 80 86 six-per-em space |
| 200655 | ** U+2007 e2 80 87 figure space |
| 200656 | ** U+2008 e2 80 88 punctuation space |
| 200657 | ** U+2009 e2 80 89 thin space |
| 200658 | ** U+200a e2 80 8a hair space |
| 200659 | ** U+2028 e2 80 a8 line separator |
| 200660 | ** U+2029 e2 80 a9 paragraph separator |
| 200661 | ** U+202f e2 80 af narrow no-break space (NNBSP) |
| 200662 | ** U+205f e2 81 9f medium mathematical space (MMSP) |
| 200663 | ** U+3000 e3 80 80 ideographical space |
| 200664 | ** U+FEFF ef bb bf byte order mark |
| 200665 | ** |
| 200666 | ** In addition, comments between '/', '*' and '*', '/' and |
| 200667 | ** from '/', '/' to end-of-line are also considered to be whitespace. |
| 200668 | */ |
| 200669 | static int json5Whitespace(const char *zIn){ |
| 200670 | int n = 0; |
| 200671 | const u8 *z = (u8*)zIn; |
| 200672 | while( 1 /*exit by "goto whitespace_done"*/ ){ |
| 200673 | switch( z[n] ){ |
| 200674 | case 0x09: |
| 200675 | case 0x0a: |
| 200676 | case 0x0b: |
| 200677 | case 0x0c: |
| 200678 | case 0x0d: |
| 200679 | case 0x20: { |
| 200680 | n++; |
| 200681 | break; |
| 200682 | } |
| 200683 | case '/': { |
| 200684 | if( z[n+1]=='*' && z[n+2]!=0 ){ |
| 200685 | int j; |
| 200686 | for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){ |
| 200687 | if( z[j]==0 ) goto whitespace_done; |
| 200688 | } |
| 200689 | n = j+1; |
| 200690 | break; |
| 200691 | }else if( z[n+1]=='/' ){ |
| 200692 | int j; |
| 200693 | char c; |
| 200694 | for(j=n+2; (c = z[j])!=0; j++){ |
| 200695 | if( c=='\n' || c=='\r' ) break; |
| 200696 | if( 0xe2==(u8)c && 0x80==(u8)z[j+1] |
| 200697 | && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]) |
| 200698 | ){ |
| 200699 | j += 2; |
| 200700 | break; |
| 200701 | } |
| 200702 | } |
| 200703 | n = j; |
| 200704 | if( z[n] ) n++; |
| 200705 | break; |
| 200706 | } |
| 200707 | goto whitespace_done; |
| 200708 | } |
| 200709 | case 0xc2: { |
| 200710 | if( z[n+1]==0xa0 ){ |
| 200711 | n += 2; |
| 200712 | break; |
| 200713 | } |
| 200714 | goto whitespace_done; |
| 200715 | } |
| 200716 | case 0xe1: { |
| 200717 | if( z[n+1]==0x9a && z[n+2]==0x80 ){ |
| 200718 | n += 3; |
| 200719 | break; |
| 200720 | } |
| 200721 | goto whitespace_done; |
| 200722 | } |
| 200723 | case 0xe2: { |
| 200724 | if( z[n+1]==0x80 ){ |
| 200725 | u8 c = z[n+2]; |
| 200726 | if( c<0x80 ) goto whitespace_done; |
| 200727 | if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){ |
| 200728 | n += 3; |
| 200729 | break; |
| 200730 | } |
| 200731 | }else if( z[n+1]==0x81 && z[n+2]==0x9f ){ |
| 200732 | n += 3; |
| 200733 | break; |
| 200734 | } |
| 200735 | goto whitespace_done; |
| 200736 | } |
| 200737 | case 0xe3: { |
| 200738 | if( z[n+1]==0x80 && z[n+2]==0x80 ){ |
| 200739 | n += 3; |
| 200740 | break; |
| 200741 | } |
| 200742 | goto whitespace_done; |
| 200743 | } |
| 200744 | case 0xef: { |
| 200745 | if( z[n+1]==0xbb && z[n+2]==0xbf ){ |
| 200746 | n += 3; |
| 200747 | break; |
| 200748 | } |
| 200749 | goto whitespace_done; |
| 200750 | } |
| 200751 | default: { |
| 200752 | goto whitespace_done; |
| 200753 | } |
| 200754 | } |
| 200755 | } |
| 200756 | whitespace_done: |
| 200757 | return n; |
| 200758 | } |
| 200759 | |
| 200760 | /* |
| 200761 | ** Extra floating-point literals to allow in JSON. |
| 200762 | */ |
| 200763 | static const struct NanInfName { |
| 200764 | char c1; |
| @@ -200172,204 +200773,455 @@ | |
| 200773 | { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, |
| 200774 | { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, |
| 200775 | { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, |
| 200776 | { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, |
| 200777 | }; |
| 200778 | |
| 200779 | /* |
| 200780 | ** Parse a single JSON value which begins at pParse->zJson[i]. Return the |
| 200781 | ** index of the first character past the end of the value parsed. |
| 200782 | ** |
| 200783 | ** Special return values: |
| 200784 | ** |
| 200785 | ** 0 End if input |
| 200786 | ** -1 Syntax error |
| 200787 | ** -2 '}' seen |
| 200788 | ** -3 ']' seen |
| 200789 | ** -4 ',' seen |
| 200790 | ** -5 ':' seen |
| 200791 | */ |
| 200792 | static int jsonParseValue(JsonParse *pParse, u32 i){ |
| 200793 | char c; |
| 200794 | u32 j; |
| 200795 | int iThis; |
| 200796 | int x; |
| 200797 | JsonNode *pNode; |
| 200798 | const char *z = pParse->zJson; |
| 200799 | json_parse_restart: |
| 200800 | switch( (u8)z[i] ){ |
| 200801 | case '{': { |
| 200802 | /* Parse object */ |
| 200803 | iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); |
| 200804 | if( iThis<0 ) return -1; |
| 200805 | if( ++pParse->iDepth > JSON_MAX_DEPTH ){ |
| 200806 | pParse->iErr = i; |
| 200807 | return -1; |
| 200808 | } |
| 200809 | for(j=i+1;;j++){ |
| 200810 | u32 nNode = pParse->nNode; |
| 200811 | x = jsonParseValue(pParse, j); |
| 200812 | if( x<=0 ){ |
| 200813 | if( x==(-2) ){ |
| 200814 | j = pParse->iErr; |
| 200815 | if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; |
| 200816 | break; |
| 200817 | } |
| 200818 | j += json5Whitespace(&z[j]); |
| 200819 | if( sqlite3JsonId1(z[j]) |
| 200820 | || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) |
| 200821 | ){ |
| 200822 | int k = j+1; |
| 200823 | while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) |
| 200824 | || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) |
| 200825 | ){ |
| 200826 | k++; |
| 200827 | } |
| 200828 | jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); |
| 200829 | pParse->hasNonstd = 1; |
| 200830 | x = k; |
| 200831 | }else{ |
| 200832 | if( x!=-1 ) pParse->iErr = j; |
| 200833 | return -1; |
| 200834 | } |
| 200835 | } |
| 200836 | if( pParse->oom ) return -1; |
| 200837 | pNode = &pParse->aNode[nNode]; |
| 200838 | if( pNode->eType!=JSON_STRING ){ |
| 200839 | pParse->iErr = j; |
| 200840 | return -1; |
| 200841 | } |
| 200842 | pNode->jnFlags |= JNODE_LABEL; |
| 200843 | j = x; |
| 200844 | if( z[j]==':' ){ |
| 200845 | j++; |
| 200846 | }else{ |
| 200847 | if( fast_isspace(z[j]) ){ |
| 200848 | do{ j++; }while( fast_isspace(z[j]) ); |
| 200849 | if( z[j]==':' ){ |
| 200850 | j++; |
| 200851 | goto parse_object_value; |
| 200852 | } |
| 200853 | } |
| 200854 | x = jsonParseValue(pParse, j); |
| 200855 | if( x!=(-5) ){ |
| 200856 | if( x!=(-1) ) pParse->iErr = j; |
| 200857 | return -1; |
| 200858 | } |
| 200859 | j = pParse->iErr+1; |
| 200860 | } |
| 200861 | parse_object_value: |
| 200862 | x = jsonParseValue(pParse, j); |
| 200863 | if( x<=0 ){ |
| 200864 | if( x!=(-1) ) pParse->iErr = j; |
| 200865 | return -1; |
| 200866 | } |
| 200867 | j = x; |
| 200868 | if( z[j]==',' ){ |
| 200869 | continue; |
| 200870 | }else if( z[j]=='}' ){ |
| 200871 | break; |
| 200872 | }else{ |
| 200873 | if( fast_isspace(z[j]) ){ |
| 200874 | do{ j++; }while( fast_isspace(z[j]) ); |
| 200875 | if( z[j]==',' ){ |
| 200876 | continue; |
| 200877 | }else if( z[j]=='}' ){ |
| 200878 | break; |
| 200879 | } |
| 200880 | } |
| 200881 | x = jsonParseValue(pParse, j); |
| 200882 | if( x==(-4) ){ |
| 200883 | j = pParse->iErr; |
| 200884 | continue; |
| 200885 | } |
| 200886 | if( x==(-2) ){ |
| 200887 | j = pParse->iErr; |
| 200888 | break; |
| 200889 | } |
| 200890 | } |
| 200891 | pParse->iErr = j; |
| 200892 | return -1; |
| 200893 | } |
| 200894 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200895 | pParse->iDepth--; |
| 200896 | return j+1; |
| 200897 | } |
| 200898 | case '[': { |
| 200899 | /* Parse array */ |
| 200900 | iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); |
| 200901 | if( iThis<0 ) return -1; |
| 200902 | if( ++pParse->iDepth > JSON_MAX_DEPTH ){ |
| 200903 | pParse->iErr = i; |
| 200904 | return -1; |
| 200905 | } |
| 200906 | memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); |
| 200907 | for(j=i+1;;j++){ |
| 200908 | x = jsonParseValue(pParse, j); |
| 200909 | if( x<=0 ){ |
| 200910 | if( x==(-3) ){ |
| 200911 | j = pParse->iErr; |
| 200912 | if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; |
| 200913 | break; |
| 200914 | } |
| 200915 | if( x!=(-1) ) pParse->iErr = j; |
| 200916 | return -1; |
| 200917 | } |
| 200918 | j = x; |
| 200919 | if( z[j]==',' ){ |
| 200920 | continue; |
| 200921 | }else if( z[j]==']' ){ |
| 200922 | break; |
| 200923 | }else{ |
| 200924 | if( fast_isspace(z[j]) ){ |
| 200925 | do{ j++; }while( fast_isspace(z[j]) ); |
| 200926 | if( z[j]==',' ){ |
| 200927 | continue; |
| 200928 | }else if( z[j]==']' ){ |
| 200929 | break; |
| 200930 | } |
| 200931 | } |
| 200932 | x = jsonParseValue(pParse, j); |
| 200933 | if( x==(-4) ){ |
| 200934 | j = pParse->iErr; |
| 200935 | continue; |
| 200936 | } |
| 200937 | if( x==(-3) ){ |
| 200938 | j = pParse->iErr; |
| 200939 | break; |
| 200940 | } |
| 200941 | } |
| 200942 | pParse->iErr = j; |
| 200943 | return -1; |
| 200944 | } |
| 200945 | pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; |
| 200946 | pParse->iDepth--; |
| 200947 | return j+1; |
| 200948 | } |
| 200949 | case '\'': { |
| 200950 | u8 jnFlags; |
| 200951 | char cDelim; |
| 200952 | pParse->hasNonstd = 1; |
| 200953 | jnFlags = JNODE_JSON5; |
| 200954 | goto parse_string; |
| 200955 | case '"': |
| 200956 | /* Parse string */ |
| 200957 | jnFlags = 0; |
| 200958 | parse_string: |
| 200959 | cDelim = z[i]; |
| 200960 | j = i+1; |
| 200961 | for(;;){ |
| 200962 | c = z[j]; |
| 200963 | if( (c & ~0x1f)==0 ){ |
| 200964 | /* Control characters are not allowed in strings */ |
| 200965 | pParse->iErr = j; |
| 200966 | return -1; |
| 200967 | } |
| 200968 | if( c=='\\' ){ |
| 200969 | c = z[++j]; |
| 200970 | if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' |
| 200971 | || c=='n' || c=='r' || c=='t' |
| 200972 | || (c=='u' && jsonIs4Hex(&z[j+1])) ){ |
| 200973 | jnFlags |= JNODE_ESCAPE; |
| 200974 | }else if( c=='\'' || c=='0' || c=='v' || c=='\n' |
| 200975 | || (0xe2==(u8)c && 0x80==(u8)z[j+1] |
| 200976 | && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) |
| 200977 | || (c=='x' && jsonIs2Hex(&z[j+1])) ){ |
| 200978 | jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); |
| 200979 | pParse->hasNonstd = 1; |
| 200980 | }else if( c=='\r' ){ |
| 200981 | if( z[j+1]=='\n' ) j++; |
| 200982 | jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); |
| 200983 | pParse->hasNonstd = 1; |
| 200984 | }else{ |
| 200985 | pParse->iErr = j; |
| 200986 | return -1; |
| 200987 | } |
| 200988 | }else if( c==cDelim ){ |
| 200989 | break; |
| 200990 | } |
| 200991 | j++; |
| 200992 | } |
| 200993 | jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); |
| 200994 | return j+1; |
| 200995 | } |
| 200996 | case 't': { |
| 200997 | if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ |
| 200998 | jsonParseAddNode(pParse, JSON_TRUE, 0, 0); |
| 200999 | return i+4; |
| 201000 | } |
| 201001 | pParse->iErr = i; |
| 201002 | return -1; |
| 201003 | } |
| 201004 | case 'f': { |
| 201005 | if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ |
| 201006 | jsonParseAddNode(pParse, JSON_FALSE, 0, 0); |
| 201007 | return i+5; |
| 201008 | } |
| 201009 | pParse->iErr = i; |
| 201010 | return -1; |
| 201011 | } |
| 201012 | case '+': { |
| 201013 | u8 seenDP, seenE, jnFlags; |
| 201014 | pParse->hasNonstd = 1; |
| 201015 | jnFlags = JNODE_JSON5; |
| 201016 | goto parse_number; |
| 201017 | case '.': |
| 201018 | if( sqlite3Isdigit(z[i+1]) ){ |
| 201019 | pParse->hasNonstd = 1; |
| 201020 | jnFlags = JNODE_JSON5; |
| 201021 | seenE = 0; |
| 201022 | seenDP = JSON_REAL; |
| 201023 | goto parse_number_2; |
| 201024 | } |
| 201025 | pParse->iErr = i; |
| 201026 | return -1; |
| 201027 | case '-': |
| 201028 | case '0': |
| 201029 | case '1': |
| 201030 | case '2': |
| 201031 | case '3': |
| 201032 | case '4': |
| 201033 | case '5': |
| 201034 | case '6': |
| 201035 | case '7': |
| 201036 | case '8': |
| 201037 | case '9': |
| 201038 | /* Parse number */ |
| 201039 | jnFlags = 0; |
| 201040 | parse_number: |
| 201041 | seenDP = JSON_INT; |
| 201042 | seenE = 0; |
| 201043 | assert( '-' < '0' ); |
| 201044 | assert( '+' < '0' ); |
| 201045 | assert( '.' < '0' ); |
| 201046 | c = z[i]; |
| 201047 | |
| 201048 | if( c<='0' ){ |
| 201049 | if( c=='0' ){ |
| 201050 | if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ |
| 201051 | assert( seenDP==JSON_INT ); |
| 201052 | pParse->hasNonstd = 1; |
| 201053 | jnFlags |= JNODE_JSON5; |
| 201054 | for(j=i+3; sqlite3Isxdigit(z[j]); j++){} |
| 201055 | goto parse_number_finish; |
| 201056 | }else if( sqlite3Isdigit(z[i+1]) ){ |
| 201057 | pParse->iErr = i+1; |
| 201058 | return -1; |
| 201059 | } |
| 201060 | }else{ |
| 201061 | if( !sqlite3Isdigit(z[i+1]) ){ |
| 201062 | /* JSON5 allows for "+Infinity" and "-Infinity" using exactly |
| 201063 | ** that case. SQLite also allows these in any case and it allows |
| 201064 | ** "+inf" and "-inf". */ |
| 201065 | if( (z[i+1]=='I' || z[i+1]=='i') |
| 201066 | && sqlite3StrNICmp(&z[i+1], "inf",3)==0 |
| 201067 | ){ |
| 201068 | pParse->hasNonstd = 1; |
| 201069 | if( z[i]=='-' ){ |
| 201070 | jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); |
| 201071 | }else{ |
| 201072 | jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); |
| 201073 | } |
| 201074 | return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); |
| 201075 | } |
| 201076 | if( z[i+1]=='.' ){ |
| 201077 | pParse->hasNonstd = 1; |
| 201078 | jnFlags |= JNODE_JSON5; |
| 201079 | goto parse_number_2; |
| 201080 | } |
| 201081 | pParse->iErr = i; |
| 201082 | return -1; |
| 201083 | } |
| 201084 | if( z[i+1]=='0' ){ |
| 201085 | if( sqlite3Isdigit(z[i+2]) ){ |
| 201086 | pParse->iErr = i+1; |
| 201087 | return -1; |
| 201088 | }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ |
| 201089 | pParse->hasNonstd = 1; |
| 201090 | jnFlags |= JNODE_JSON5; |
| 201091 | for(j=i+4; sqlite3Isxdigit(z[j]); j++){} |
| 201092 | goto parse_number_finish; |
| 201093 | } |
| 201094 | } |
| 201095 | } |
| 201096 | } |
| 201097 | parse_number_2: |
| 201098 | for(j=i+1;; j++){ |
| 201099 | c = z[j]; |
| 201100 | if( sqlite3Isdigit(c) ) continue; |
| 201101 | if( c=='.' ){ |
| 201102 | if( seenDP==JSON_REAL ){ |
| 201103 | pParse->iErr = j; |
| 201104 | return -1; |
| 201105 | } |
| 201106 | seenDP = JSON_REAL; |
| 201107 | continue; |
| 201108 | } |
| 201109 | if( c=='e' || c=='E' ){ |
| 201110 | if( z[j-1]<'0' ){ |
| 201111 | if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ |
| 201112 | pParse->hasNonstd = 1; |
| 201113 | jnFlags |= JNODE_JSON5; |
| 201114 | }else{ |
| 201115 | pParse->iErr = j; |
| 201116 | return -1; |
| 201117 | } |
| 201118 | } |
| 201119 | if( seenE ){ |
| 201120 | pParse->iErr = j; |
| 201121 | return -1; |
| 201122 | } |
| 201123 | seenDP = JSON_REAL; |
| 201124 | seenE = 1; |
| 201125 | c = z[j+1]; |
| 201126 | if( c=='+' || c=='-' ){ |
| 201127 | j++; |
| 201128 | c = z[j+1]; |
| 201129 | } |
| 201130 | if( c<'0' || c>'9' ){ |
| 201131 | pParse->iErr = j; |
| 201132 | return -1; |
| 201133 | } |
| 201134 | continue; |
| 201135 | } |
| 201136 | break; |
| 201137 | } |
| 201138 | if( z[j-1]<'0' ){ |
| 201139 | if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ |
| 201140 | pParse->hasNonstd = 1; |
| 201141 | jnFlags |= JNODE_JSON5; |
| 201142 | }else{ |
| 201143 | pParse->iErr = j; |
| 201144 | return -1; |
| 201145 | } |
| 201146 | } |
| 201147 | parse_number_finish: |
| 201148 | jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); |
| 201149 | return j; |
| 201150 | } |
| 201151 | case '}': { |
| 201152 | pParse->iErr = i; |
| 201153 | return -2; /* End of {...} */ |
| 201154 | } |
| 201155 | case ']': { |
| 201156 | pParse->iErr = i; |
| 201157 | return -3; /* End of [...] */ |
| 201158 | } |
| 201159 | case ',': { |
| 201160 | pParse->iErr = i; |
| 201161 | return -4; /* List separator */ |
| 201162 | } |
| 201163 | case ':': { |
| 201164 | pParse->iErr = i; |
| 201165 | return -5; /* Object label/value separator */ |
| 201166 | } |
| 201167 | case 0: { |
| 201168 | return 0; /* End of file */ |
| 201169 | } |
| 201170 | case 0x09: |
| 201171 | case 0x0a: |
| 201172 | case 0x0d: |
| 201173 | case 0x20: { |
| 201174 | do{ |
| 201175 | i++; |
| 201176 | }while( fast_isspace(z[i]) ); |
| 201177 | goto json_parse_restart; |
| 201178 | } |
| 201179 | case 0x0b: |
| 201180 | case 0x0c: |
| 201181 | case '/': |
| 201182 | case 0xc2: |
| 201183 | case 0xe1: |
| 201184 | case 0xe2: |
| 201185 | case 0xe3: |
| 201186 | case 0xef: { |
| 201187 | j = json5Whitespace(&z[i]); |
| 201188 | if( j>0 ){ |
| 201189 | i += j; |
| 201190 | pParse->hasNonstd = 1; |
| 201191 | goto json_parse_restart; |
| 201192 | } |
| 201193 | pParse->iErr = i; |
| 201194 | return -1; |
| 201195 | } |
| 201196 | case 'n': { |
| 201197 | if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ |
| 201198 | jsonParseAddNode(pParse, JSON_NULL, 0, 0); |
| 201199 | return i+4; |
| 201200 | } |
| 201201 | /* fall-through into the default case that checks for NaN */ |
| 201202 | } |
| 201203 | default: { |
| 201204 | u32 k; |
| 201205 | int nn; |
| 201206 | c = z[i]; |
| 201207 | for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){ |
| 201208 | if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue; |
| 201209 | nn = aNanInfName[k].n; |
| 201210 | if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){ |
| 201211 | continue; |
| 201212 | } |
| 201213 | if( sqlite3Isalnum(z[i+nn]) ) continue; |
| 201214 | jsonParseAddNode(pParse, aNanInfName[k].eType, |
| 201215 | aNanInfName[k].nRepl, aNanInfName[k].zRepl); |
| 201216 | pParse->hasNonstd = 1; |
| 201217 | return i + nn; |
| 201218 | } |
| 201219 | pParse->iErr = i; |
| 201220 | return -1; /* Syntax error */ |
| 201221 | } |
| 201222 | } /* End switch(z[i]) */ |
| 201223 | } |
| 201224 | |
| 201225 | /* |
| 201226 | ** Parse a complete JSON string. Return 0 on success or non-zero if there |
| 201227 | ** are any errors. If an error occurs, free all memory associated with |
| @@ -200389,11 +201241,18 @@ | |
| 201241 | i = jsonParseValue(pParse, 0); |
| 201242 | if( pParse->oom ) i = -1; |
| 201243 | if( i>0 ){ |
| 201244 | assert( pParse->iDepth==0 ); |
| 201245 | while( fast_isspace(zJson[i]) ) i++; |
| 201246 | if( zJson[i] ){ |
| 201247 | i += json5Whitespace(&zJson[i]); |
| 201248 | if( zJson[i] ){ |
| 201249 | jsonParseReset(pParse); |
| 201250 | return 1; |
| 201251 | } |
| 201252 | pParse->hasNonstd = 1; |
| 201253 | } |
| 201254 | } |
| 201255 | if( i<=0 ){ |
| 201256 | if( pCtx!=0 ){ |
| 201257 | if( pParse->oom ){ |
| 201258 | sqlite3_result_error_nomem(pCtx); |
| @@ -200460,10 +201319,19 @@ | |
| 201319 | ** of the argv array. Use the sqlite3_get_auxdata() cache for this |
| 201320 | ** parse if it is available. If the cache is not available or if it |
| 201321 | ** is no longer valid, parse the JSON again and return the new parse, |
| 201322 | ** and also register the new parse so that it will be available for |
| 201323 | ** future sqlite3_get_auxdata() calls. |
| 201324 | ** |
| 201325 | ** If an error occurs and pErrCtx!=0 then report the error on pErrCtx |
| 201326 | ** and return NULL. |
| 201327 | ** |
| 201328 | ** If an error occurs and pErrCtx==0 then return the Parse object with |
| 201329 | ** JsonParse.nErr non-zero. If the caller invokes this routine with |
| 201330 | ** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller |
| 201331 | ** is responsible for invoking jsonParseFree() on the returned value. |
| 201332 | ** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0. |
| 201333 | */ |
| 201334 | static JsonParse *jsonParseCached( |
| 201335 | sqlite3_context *pCtx, |
| 201336 | sqlite3_value **argv, |
| 201337 | sqlite3_context *pErrCtx |
| @@ -200509,10 +201377,14 @@ | |
| 201377 | } |
| 201378 | memset(p, 0, sizeof(*p)); |
| 201379 | p->zJson = (char*)&p[1]; |
| 201380 | memcpy((char*)p->zJson, zJson, nJson+1); |
| 201381 | if( jsonParse(p, pErrCtx, p->zJson) ){ |
| 201382 | if( pErrCtx==0 ){ |
| 201383 | p->nErr = 1; |
| 201384 | return p; |
| 201385 | } |
| 201386 | sqlite3_free(p); |
| 201387 | return 0; |
| 201388 | } |
| 201389 | p->nJson = nJson; |
| 201390 | p->iHold = iMaxHold+1; |
| @@ -200523,19 +201395,28 @@ | |
| 201395 | |
| 201396 | /* |
| 201397 | ** Compare the OBJECT label at pNode against zKey,nKey. Return true on |
| 201398 | ** a match. |
| 201399 | */ |
| 201400 | static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ |
| 201401 | assert( pNode->eU==1 ); |
| 201402 | if( pNode->jnFlags & JNODE_RAW ){ |
| 201403 | if( pNode->n!=nKey ) return 0; |
| 201404 | return strncmp(pNode->u.zJContent, zKey, nKey)==0; |
| 201405 | }else{ |
| 201406 | if( pNode->n!=nKey+2 ) return 0; |
| 201407 | return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; |
| 201408 | } |
| 201409 | } |
| 201410 | static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ |
| 201411 | if( p1->jnFlags & JNODE_RAW ){ |
| 201412 | return jsonLabelCompare(p2, p1->u.zJContent, p1->n); |
| 201413 | }else if( p2->jnFlags & JNODE_RAW ){ |
| 201414 | return jsonLabelCompare(p1, p2->u.zJContent, p2->n); |
| 201415 | }else{ |
| 201416 | return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; |
| 201417 | } |
| 201418 | } |
| 201419 | |
| 201420 | /* forward declaration */ |
| 201421 | static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); |
| 201422 | |
| @@ -201003,11 +201884,11 @@ | |
| 201884 | if( argc==2 ){ |
| 201885 | /* With a single PATH argument */ |
| 201886 | zPath = (const char*)sqlite3_value_text(argv[1]); |
| 201887 | if( zPath==0 ) return; |
| 201888 | if( flags & JSON_ABPATH ){ |
| 201889 | if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){ |
| 201890 | /* The -> and ->> operators accept abbreviated PATH arguments. This |
| 201891 | ** is mostly for compatibility with PostgreSQL, but also for |
| 201892 | ** convenience. |
| 201893 | ** |
| 201894 | ** NUMBER ==> $[NUMBER] // PG compatible |
| @@ -201094,16 +201975,14 @@ | |
| 201975 | assert( pPatch[i].eType==JSON_STRING ); |
| 201976 | assert( pPatch[i].jnFlags & JNODE_LABEL ); |
| 201977 | assert( pPatch[i].eU==1 ); |
| 201978 | nKey = pPatch[i].n; |
| 201979 | zKey = pPatch[i].u.zJContent; |
| 201980 | for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){ |
| 201981 | assert( pTarget[j].eType==JSON_STRING ); |
| 201982 | assert( pTarget[j].jnFlags & JNODE_LABEL ); |
| 201983 | if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ |
| 201984 | if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break; |
| 201985 | if( pPatch[i+1].eType==JSON_NULL ){ |
| 201986 | pTarget[j+1].jnFlags |= JNODE_REMOVE; |
| 201987 | }else{ |
| 201988 | JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); |
| @@ -201386,22 +202265,83 @@ | |
| 202265 | } |
| 202266 | |
| 202267 | /* |
| 202268 | ** json_valid(JSON) |
| 202269 | ** |
| 202270 | ** Return 1 if JSON is a well-formed canonical JSON string according |
| 202271 | ** to RFC-7159. Return 0 otherwise. |
| 202272 | */ |
| 202273 | static void jsonValidFunc( |
| 202274 | sqlite3_context *ctx, |
| 202275 | int argc, |
| 202276 | sqlite3_value **argv |
| 202277 | ){ |
| 202278 | JsonParse *p; /* The parse */ |
| 202279 | UNUSED_PARAMETER(argc); |
| 202280 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 202281 | p = jsonParseCached(ctx, argv, 0); |
| 202282 | if( p==0 || p->oom ){ |
| 202283 | sqlite3_result_error_nomem(ctx); |
| 202284 | sqlite3_free(p); |
| 202285 | }else{ |
| 202286 | sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0); |
| 202287 | if( p->nErr ) jsonParseFree(p); |
| 202288 | } |
| 202289 | } |
| 202290 | |
| 202291 | /* |
| 202292 | ** json_error_position(JSON) |
| 202293 | ** |
| 202294 | ** If the argument is not an interpretable JSON string, then return the 1-based |
| 202295 | ** character position at which the parser first recognized that the input |
| 202296 | ** was in error. The left-most character is 1. If the string is valid |
| 202297 | ** JSON, then return 0. |
| 202298 | ** |
| 202299 | ** Note that json_valid() is only true for strictly conforming canonical JSON. |
| 202300 | ** But this routine returns zero if the input contains extension. Thus: |
| 202301 | ** |
| 202302 | ** (1) If the input X is strictly conforming canonical JSON: |
| 202303 | ** |
| 202304 | ** json_valid(X) returns true |
| 202305 | ** json_error_position(X) returns 0 |
| 202306 | ** |
| 202307 | ** (2) If the input X is JSON but it includes extension (such as JSON5) that |
| 202308 | ** are not part of RFC-8259: |
| 202309 | ** |
| 202310 | ** json_valid(X) returns false |
| 202311 | ** json_error_position(X) return 0 |
| 202312 | ** |
| 202313 | ** (3) If the input X cannot be interpreted as JSON even taking extensions |
| 202314 | ** into account: |
| 202315 | ** |
| 202316 | ** json_valid(X) return false |
| 202317 | ** json_error_position(X) returns 1 or more |
| 202318 | */ |
| 202319 | static void jsonErrorFunc( |
| 202320 | sqlite3_context *ctx, |
| 202321 | int argc, |
| 202322 | sqlite3_value **argv |
| 202323 | ){ |
| 202324 | JsonParse *p; /* The parse */ |
| 202325 | UNUSED_PARAMETER(argc); |
| 202326 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 202327 | p = jsonParseCached(ctx, argv, 0); |
| 202328 | if( p==0 || p->oom ){ |
| 202329 | sqlite3_result_error_nomem(ctx); |
| 202330 | sqlite3_free(p); |
| 202331 | }else if( p->nErr==0 ){ |
| 202332 | sqlite3_result_int(ctx, 0); |
| 202333 | }else{ |
| 202334 | int n = 1; |
| 202335 | u32 i; |
| 202336 | const char *z = p->zJson; |
| 202337 | for(i=0; i<p->iErr && ALWAYS(z[i]); i++){ |
| 202338 | if( (z[i]&0xc0)!=0x80 ) n++; |
| 202339 | } |
| 202340 | sqlite3_result_int(ctx, n); |
| 202341 | jsonParseFree(p); |
| 202342 | } |
| 202343 | } |
| 202344 | |
| 202345 | |
| 202346 | /**************************************************************************** |
| 202347 | ** Aggregate SQL function implementations |
| @@ -201741,18 +202681,20 @@ | |
| 202681 | assert( pNode->eType==JSON_STRING ); |
| 202682 | assert( pNode->jnFlags & JNODE_LABEL ); |
| 202683 | assert( pNode->eU==1 ); |
| 202684 | z = pNode->u.zJContent; |
| 202685 | nn = pNode->n; |
| 202686 | if( (pNode->jnFlags & JNODE_RAW)==0 ){ |
| 202687 | assert( nn>=2 ); |
| 202688 | assert( z[0]=='"' || z[0]=='\'' ); |
| 202689 | assert( z[nn-1]=='"' || z[0]=='\'' ); |
| 202690 | if( nn>2 && sqlite3Isalpha(z[1]) ){ |
| 202691 | for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){} |
| 202692 | if( jj==nn-1 ){ |
| 202693 | z++; |
| 202694 | nn -= 2; |
| 202695 | } |
| 202696 | } |
| 202697 | } |
| 202698 | jsonPrintf(nn+2, pStr, ".%.*s", nn, z); |
| 202699 | } |
| 202700 | |
| @@ -202108,10 +203050,11 @@ | |
| 203050 | static FuncDef aJsonFunc[] = { |
| 203051 | JFUNCTION(json, 1, 0, jsonRemoveFunc), |
| 203052 | JFUNCTION(json_array, -1, 0, jsonArrayFunc), |
| 203053 | JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), |
| 203054 | JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), |
| 203055 | JFUNCTION(json_error_position,1, 0, jsonErrorFunc), |
| 203056 | JFUNCTION(json_extract, -1, 0, jsonExtractFunc), |
| 203057 | JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), |
| 203058 | JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), |
| 203059 | JFUNCTION(json_insert, -1, 0, jsonSetFunc), |
| 203060 | JFUNCTION(json_object, -1, 0, jsonObjectFunc), |
| @@ -202632,20 +203575,21 @@ | |
| 203575 | ** using C-preprocessor macros. If that is unsuccessful, or if |
| 203576 | ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined |
| 203577 | ** at run-time. |
| 203578 | */ |
| 203579 | #ifndef SQLITE_BYTEORDER |
| 203580 | # if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ |
| 203581 | defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ |
| 203582 | defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ |
| 203583 | defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) |
| 203584 | # define SQLITE_BYTEORDER 1234 |
| 203585 | # elif defined(sparc) || defined(__ppc__) || \ |
| 203586 | defined(__ARMEB__) || defined(__AARCH64EB__) |
| 203587 | # define SQLITE_BYTEORDER 4321 |
| 203588 | # else |
| 203589 | # define SQLITE_BYTEORDER 0 |
| 203590 | # endif |
| 203591 | #endif |
| 203592 | |
| 203593 | |
| 203594 | /* What version of MSVC is being used. 0 means MSVC is not being used */ |
| 203595 | #ifndef MSVC_VERSION |
| @@ -213201,15 +214145,22 @@ | |
| 214145 | static int rbuLockDatabase(sqlite3 *db){ |
| 214146 | int rc = SQLITE_OK; |
| 214147 | sqlite3_file *fd = 0; |
| 214148 | |
| 214149 | sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); |
| 214150 | if( fd ){ |
| 214151 | sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); |
| 214152 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); |
| 214153 | if( rc==SQLITE_OK ){ |
| 214154 | rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE); |
| 214155 | } |
| 214156 | sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd); |
| 214157 | }else{ |
| 214158 | sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd); |
| 214159 | } |
| 214160 | |
| 214161 | if( rc==SQLITE_OK && fd->pMethods ){ |
| 214162 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED); |
| 214163 | if( rc==SQLITE_OK ){ |
| 214164 | rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE); |
| 214165 | } |
| 214166 | } |
| @@ -216825,10 +217776,12 @@ | |
| 217776 | # define SESSIONS_STRM_CHUNK_SIZE 64 |
| 217777 | # else |
| 217778 | # define SESSIONS_STRM_CHUNK_SIZE 1024 |
| 217779 | # endif |
| 217780 | #endif |
| 217781 | |
| 217782 | #define SESSIONS_ROWID "_rowid_" |
| 217783 | |
| 217784 | static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; |
| 217785 | |
| 217786 | typedef struct SessionHook SessionHook; |
| 217787 | struct SessionHook { |
| @@ -216847,10 +217800,11 @@ | |
| 217800 | char *zDb; /* Name of database session is attached to */ |
| 217801 | int bEnableSize; /* True if changeset_size() enabled */ |
| 217802 | int bEnable; /* True if currently recording */ |
| 217803 | int bIndirect; /* True if all changes are indirect */ |
| 217804 | int bAutoAttach; /* True to auto-attach tables */ |
| 217805 | int bImplicitPK; /* True to handle tables with implicit PK */ |
| 217806 | int rc; /* Non-zero if an error has occurred */ |
| 217807 | void *pFilterCtx; /* First argument to pass to xTableFilter */ |
| 217808 | int (*xTableFilter)(void *pCtx, const char *zTab); |
| 217809 | i64 nMalloc; /* Number of bytes of data allocated */ |
| 217810 | i64 nMaxChangesetSize; |
| @@ -216923,10 +217877,11 @@ | |
| 217877 | struct SessionTable { |
| 217878 | SessionTable *pNext; |
| 217879 | char *zName; /* Local name of table */ |
| 217880 | int nCol; /* Number of columns in table zName */ |
| 217881 | int bStat1; /* True if this is sqlite_stat1 */ |
| 217882 | int bRowid; /* True if this table uses rowid for PK */ |
| 217883 | const char **azCol; /* Column names */ |
| 217884 | u8 *abPK; /* Array of primary key flags */ |
| 217885 | int nEntry; /* Total number of entries in hash table */ |
| 217886 | int nChange; /* Size of apChange[] array */ |
| 217887 | SessionChange **apChange; /* Hash table buckets */ |
| @@ -217315,60 +218270,66 @@ | |
| 218270 | ** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned |
| 218271 | ** and the output variables are set as described above. |
| 218272 | */ |
| 218273 | static int sessionPreupdateHash( |
| 218274 | sqlite3_session *pSession, /* Session object that owns pTab */ |
| 218275 | i64 iRowid, |
| 218276 | SessionTable *pTab, /* Session table handle */ |
| 218277 | int bNew, /* True to hash the new.* PK */ |
| 218278 | int *piHash, /* OUT: Hash value */ |
| 218279 | int *pbNullPK /* OUT: True if there are NULL values in PK */ |
| 218280 | ){ |
| 218281 | unsigned int h = 0; /* Hash value to return */ |
| 218282 | int i; /* Used to iterate through columns */ |
| 218283 | |
| 218284 | if( pTab->bRowid ){ |
| 218285 | assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) ); |
| 218286 | h = sessionHashAppendI64(h, iRowid); |
| 218287 | }else{ |
| 218288 | assert( *pbNullPK==0 ); |
| 218289 | assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); |
| 218290 | for(i=0; i<pTab->nCol; i++){ |
| 218291 | if( pTab->abPK[i] ){ |
| 218292 | int rc; |
| 218293 | int eType; |
| 218294 | sqlite3_value *pVal; |
| 218295 | |
| 218296 | if( bNew ){ |
| 218297 | rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); |
| 218298 | }else{ |
| 218299 | rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); |
| 218300 | } |
| 218301 | if( rc!=SQLITE_OK ) return rc; |
| 218302 | |
| 218303 | eType = sqlite3_value_type(pVal); |
| 218304 | h = sessionHashAppendType(h, eType); |
| 218305 | if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ |
| 218306 | i64 iVal; |
| 218307 | if( eType==SQLITE_INTEGER ){ |
| 218308 | iVal = sqlite3_value_int64(pVal); |
| 218309 | }else{ |
| 218310 | double rVal = sqlite3_value_double(pVal); |
| 218311 | assert( sizeof(iVal)==8 && sizeof(rVal)==8 ); |
| 218312 | memcpy(&iVal, &rVal, 8); |
| 218313 | } |
| 218314 | h = sessionHashAppendI64(h, iVal); |
| 218315 | }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ |
| 218316 | const u8 *z; |
| 218317 | int n; |
| 218318 | if( eType==SQLITE_TEXT ){ |
| 218319 | z = (const u8 *)sqlite3_value_text(pVal); |
| 218320 | }else{ |
| 218321 | z = (const u8 *)sqlite3_value_blob(pVal); |
| 218322 | } |
| 218323 | n = sqlite3_value_bytes(pVal); |
| 218324 | if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM; |
| 218325 | h = sessionHashAppendBlob(h, n, z); |
| 218326 | }else{ |
| 218327 | assert( eType==SQLITE_NULL ); |
| 218328 | assert( pTab->bStat1==0 || i!=1 ); |
| 218329 | *pbNullPK = 1; |
| 218330 | } |
| 218331 | } |
| 218332 | } |
| 218333 | } |
| 218334 | |
| 218335 | *piHash = (h % pTab->nChange); |
| @@ -217647,16 +218608,22 @@ | |
| 218608 | ** if the pre-update-hook does not affect the same row as pChange, it returns |
| 218609 | ** false. |
| 218610 | */ |
| 218611 | static int sessionPreupdateEqual( |
| 218612 | sqlite3_session *pSession, /* Session object that owns SessionTable */ |
| 218613 | i64 iRowid, /* Rowid value if pTab->bRowid */ |
| 218614 | SessionTable *pTab, /* Table associated with change */ |
| 218615 | SessionChange *pChange, /* Change to compare to */ |
| 218616 | int op /* Current pre-update operation */ |
| 218617 | ){ |
| 218618 | int iCol; /* Used to iterate through columns */ |
| 218619 | u8 *a = pChange->aRecord; /* Cursor used to scan change record */ |
| 218620 | |
| 218621 | if( pTab->bRowid ){ |
| 218622 | if( a[0]!=SQLITE_INTEGER ) return 0; |
| 218623 | return sessionGetI64(&a[1])==iRowid; |
| 218624 | } |
| 218625 | |
| 218626 | assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); |
| 218627 | for(iCol=0; iCol<pTab->nCol; iCol++){ |
| 218628 | if( !pTab->abPK[iCol] ){ |
| 218629 | a += sessionSerialLen(a); |
| @@ -217798,11 +218765,12 @@ | |
| 218765 | const char *zDb, /* Name of attached database (e.g. "main") */ |
| 218766 | const char *zThis, /* Table name */ |
| 218767 | int *pnCol, /* OUT: number of columns */ |
| 218768 | const char **pzTab, /* OUT: Copy of zThis */ |
| 218769 | const char ***pazCol, /* OUT: Array of column names for table */ |
| 218770 | u8 **pabPK, /* OUT: Array of booleans - true for PK col */ |
| 218771 | int *pbRowid /* OUT: True if only PK is a rowid */ |
| 218772 | ){ |
| 218773 | char *zPragma; |
| 218774 | sqlite3_stmt *pStmt; |
| 218775 | int rc; |
| 218776 | sqlite3_int64 nByte; |
| @@ -217810,10 +218778,11 @@ | |
| 218778 | int nThis; |
| 218779 | int i; |
| 218780 | u8 *pAlloc = 0; |
| 218781 | char **azCol = 0; |
| 218782 | u8 *abPK = 0; |
| 218783 | int bRowid = 0; /* Set to true to use rowid as PK */ |
| 218784 | |
| 218785 | assert( pazCol && pabPK ); |
| 218786 | |
| 218787 | nThis = sqlite3Strlen30(zThis); |
| 218788 | if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ |
| @@ -217854,14 +218823,19 @@ | |
| 218823 | if( pzTab ) *pzTab = 0; |
| 218824 | return rc; |
| 218825 | } |
| 218826 | |
| 218827 | nByte = nThis + 1; |
| 218828 | bRowid = (pbRowid!=0); |
| 218829 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218830 | nByte += sqlite3_column_bytes(pStmt, 1); |
| 218831 | nDbCol++; |
| 218832 | if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; |
| 218833 | } |
| 218834 | if( nDbCol==0 ) bRowid = 0; |
| 218835 | nDbCol += bRowid; |
| 218836 | nByte += strlen(SESSIONS_ROWID); |
| 218837 | rc = sqlite3_reset(pStmt); |
| 218838 | |
| 218839 | if( rc==SQLITE_OK ){ |
| 218840 | nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); |
| 218841 | pAlloc = sessionMalloc64(pSession, nByte); |
| @@ -217879,10 +218853,18 @@ | |
| 218853 | *pzTab = (char *)pAlloc; |
| 218854 | pAlloc += nThis+1; |
| 218855 | } |
| 218856 | |
| 218857 | i = 0; |
| 218858 | if( bRowid ){ |
| 218859 | size_t nName = strlen(SESSIONS_ROWID); |
| 218860 | memcpy(pAlloc, SESSIONS_ROWID, nName+1); |
| 218861 | azCol[i] = (char*)pAlloc; |
| 218862 | pAlloc += nName+1; |
| 218863 | abPK[i] = 1; |
| 218864 | i++; |
| 218865 | } |
| 218866 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 218867 | int nName = sqlite3_column_bytes(pStmt, 1); |
| 218868 | const unsigned char *zName = sqlite3_column_text(pStmt, 1); |
| 218869 | if( zName==0 ) break; |
| 218870 | memcpy(pAlloc, zName, nName+1); |
| @@ -217890,11 +218872,10 @@ | |
| 218872 | pAlloc += nName+1; |
| 218873 | abPK[i] = sqlite3_column_int(pStmt, 5); |
| 218874 | i++; |
| 218875 | } |
| 218876 | rc = sqlite3_reset(pStmt); |
| 218877 | } |
| 218878 | |
| 218879 | /* If successful, populate the output variables. Otherwise, zero them and |
| 218880 | ** free any allocation made. An error code will be returned in this case. |
| 218881 | */ |
| @@ -217907,10 +218888,11 @@ | |
| 218888 | *pabPK = 0; |
| 218889 | *pnCol = 0; |
| 218890 | if( pzTab ) *pzTab = 0; |
| 218891 | sessionFree(pSession, azCol); |
| 218892 | } |
| 218893 | if( pbRowid ) *pbRowid = bRowid; |
| 218894 | sqlite3_finalize(pStmt); |
| 218895 | return rc; |
| 218896 | } |
| 218897 | |
| 218898 | /* |
| @@ -217928,11 +218910,12 @@ | |
| 218910 | static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ |
| 218911 | if( pTab->nCol==0 ){ |
| 218912 | u8 *abPK; |
| 218913 | assert( pTab->azCol==0 || pTab->abPK==0 ); |
| 218914 | pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, |
| 218915 | pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK, |
| 218916 | (pSession->bImplicitPK ? &pTab->bRowid : 0) |
| 218917 | ); |
| 218918 | if( pSession->rc==SQLITE_OK ){ |
| 218919 | int i; |
| 218920 | for(i=0; i<pTab->nCol; i++){ |
| 218921 | if( abPK[i] ){ |
| @@ -218000,10 +218983,11 @@ | |
| 218983 | SessionTable *pTab, /* Table that change applies to */ |
| 218984 | SessionChange *pC /* Update pC->nMaxSize */ |
| 218985 | ){ |
| 218986 | i64 nNew = 2; |
| 218987 | if( pC->op==SQLITE_INSERT ){ |
| 218988 | if( pTab->bRowid ) nNew += 9; |
| 218989 | if( op!=SQLITE_DELETE ){ |
| 218990 | int ii; |
| 218991 | for(ii=0; ii<pTab->nCol; ii++){ |
| 218992 | sqlite3_value *p = 0; |
| 218993 | pSession->hook.xNew(pSession->hook.pCtx, ii, &p); |
| @@ -218016,16 +219000,20 @@ | |
| 219000 | nNew += pC->nRecord; |
| 219001 | } |
| 219002 | }else{ |
| 219003 | int ii; |
| 219004 | u8 *pCsr = pC->aRecord; |
| 219005 | if( pTab->bRowid ){ |
| 219006 | nNew += 9 + 1; |
| 219007 | pCsr += 9; |
| 219008 | } |
| 219009 | for(ii=pTab->bRowid; ii<pTab->nCol; ii++){ |
| 219010 | int bChanged = 1; |
| 219011 | int nOld = 0; |
| 219012 | int eType; |
| 219013 | sqlite3_value *p = 0; |
| 219014 | pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p); |
| 219015 | if( p==0 ){ |
| 219016 | return SQLITE_NOMEM; |
| 219017 | } |
| 219018 | |
| 219019 | eType = *pCsr++; |
| @@ -218100,10 +219088,11 @@ | |
| 219088 | ** Unless one is already present or an error occurs, an entry is added |
| 219089 | ** to the changed-rows hash table associated with table pTab. |
| 219090 | */ |
| 219091 | static void sessionPreupdateOneChange( |
| 219092 | int op, /* One of SQLITE_UPDATE, INSERT, DELETE */ |
| 219093 | i64 iRowid, |
| 219094 | sqlite3_session *pSession, /* Session object pTab is attached to */ |
| 219095 | SessionTable *pTab /* Table that change applies to */ |
| 219096 | ){ |
| 219097 | int iHash; |
| 219098 | int bNull = 0; |
| @@ -218115,11 +219104,11 @@ | |
| 219104 | /* Load table details if required */ |
| 219105 | if( sessionInitTable(pSession, pTab) ) return; |
| 219106 | |
| 219107 | /* Check the number of columns in this xPreUpdate call matches the |
| 219108 | ** number of columns in the table. */ |
| 219109 | if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){ |
| 219110 | pSession->rc = SQLITE_SCHEMA; |
| 219111 | return; |
| 219112 | } |
| 219113 | |
| 219114 | /* Grow the hash table if required */ |
| @@ -218148,18 +219137,20 @@ | |
| 219137 | } |
| 219138 | |
| 219139 | /* Calculate the hash-key for this change. If the primary key of the row |
| 219140 | ** includes a NULL value, exit early. Such changes are ignored by the |
| 219141 | ** session module. */ |
| 219142 | rc = sessionPreupdateHash( |
| 219143 | pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull |
| 219144 | ); |
| 219145 | if( rc!=SQLITE_OK ) goto error_out; |
| 219146 | |
| 219147 | if( bNull==0 ){ |
| 219148 | /* Search the hash table for an existing record for this row. */ |
| 219149 | SessionChange *pC; |
| 219150 | for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){ |
| 219151 | if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break; |
| 219152 | } |
| 219153 | |
| 219154 | if( pC==0 ){ |
| 219155 | /* Create a new change object containing all the old values (if |
| 219156 | ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK |
| @@ -218170,11 +219161,11 @@ | |
| 219161 | assert( rc==SQLITE_OK ); |
| 219162 | pTab->nEntry++; |
| 219163 | |
| 219164 | /* Figure out how large an allocation is required */ |
| 219165 | nByte = sizeof(SessionChange); |
| 219166 | for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ |
| 219167 | sqlite3_value *p = 0; |
| 219168 | if( op!=SQLITE_INSERT ){ |
| 219169 | TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 219170 | assert( trc==SQLITE_OK ); |
| 219171 | }else if( pTab->abPK[i] ){ |
| @@ -218185,10 +219176,13 @@ | |
| 219176 | /* This may fail if SQLite value p contains a utf-16 string that must |
| 219177 | ** be converted to utf-8 and an OOM error occurs while doing so. */ |
| 219178 | rc = sessionSerializeValue(0, p, &nByte); |
| 219179 | if( rc!=SQLITE_OK ) goto error_out; |
| 219180 | } |
| 219181 | if( pTab->bRowid ){ |
| 219182 | nByte += 9; /* Size of rowid field - an integer */ |
| 219183 | } |
| 219184 | |
| 219185 | /* Allocate the change object */ |
| 219186 | pC = (SessionChange *)sessionMalloc64(pSession, nByte); |
| 219187 | if( !pC ){ |
| 219188 | rc = SQLITE_NOMEM; |
| @@ -218201,11 +219195,16 @@ | |
| 219195 | /* Populate the change object. None of the preupdate_old(), |
| 219196 | ** preupdate_new() or SerializeValue() calls below may fail as all |
| 219197 | ** required values and encodings have already been cached in memory. |
| 219198 | ** It is not possible for an OOM to occur in this block. */ |
| 219199 | nByte = 0; |
| 219200 | if( pTab->bRowid ){ |
| 219201 | pC->aRecord[0] = SQLITE_INTEGER; |
| 219202 | sessionPutI64(&pC->aRecord[1], iRowid); |
| 219203 | nByte = 9; |
| 219204 | } |
| 219205 | for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ |
| 219206 | sqlite3_value *p = 0; |
| 219207 | if( op!=SQLITE_INSERT ){ |
| 219208 | pSession->hook.xOld(pSession->hook.pCtx, i, &p); |
| 219209 | }else if( pTab->abPK[i] ){ |
| 219210 | pSession->hook.xNew(pSession->hook.pCtx, i, &p); |
| @@ -218316,13 +219315,14 @@ | |
| 219315 | if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue; |
| 219316 | |
| 219317 | pSession->rc = sessionFindTable(pSession, zName, &pTab); |
| 219318 | if( pTab ){ |
| 219319 | assert( pSession->rc==SQLITE_OK ); |
| 219320 | assert( op==SQLITE_UPDATE || iKey1==iKey2 ); |
| 219321 | sessionPreupdateOneChange(op, iKey1, pSession, pTab); |
| 219322 | if( op==SQLITE_UPDATE ){ |
| 219323 | sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab); |
| 219324 | } |
| 219325 | } |
| 219326 | } |
| 219327 | } |
| 219328 | |
| @@ -218357,29 +219357,30 @@ | |
| 219357 | } |
| 219358 | |
| 219359 | typedef struct SessionDiffCtx SessionDiffCtx; |
| 219360 | struct SessionDiffCtx { |
| 219361 | sqlite3_stmt *pStmt; |
| 219362 | int bRowid; |
| 219363 | int nOldOff; |
| 219364 | }; |
| 219365 | |
| 219366 | /* |
| 219367 | ** The diff hook implementations. |
| 219368 | */ |
| 219369 | static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 219370 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 219371 | *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid); |
| 219372 | return SQLITE_OK; |
| 219373 | } |
| 219374 | static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){ |
| 219375 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 219376 | *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid); |
| 219377 | return SQLITE_OK; |
| 219378 | } |
| 219379 | static int sessionDiffCount(void *pCtx){ |
| 219380 | SessionDiffCtx *p = (SessionDiffCtx*)pCtx; |
| 219381 | return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid; |
| 219382 | } |
| 219383 | static int sessionDiffDepth(void *pCtx){ |
| 219384 | (void)pCtx; |
| 219385 | return 0; |
| 219386 | } |
| @@ -218454,18 +219455,20 @@ | |
| 219455 | } |
| 219456 | |
| 219457 | static char *sessionSelectFindNew( |
| 219458 | const char *zDb1, /* Pick rows in this db only */ |
| 219459 | const char *zDb2, /* But not in this one */ |
| 219460 | int bRowid, |
| 219461 | const char *zTbl, /* Table name */ |
| 219462 | const char *zExpr |
| 219463 | ){ |
| 219464 | const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*"); |
| 219465 | char *zRet = sqlite3_mprintf( |
| 219466 | "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS (" |
| 219467 | " SELECT 1 FROM \"%w\".\"%w\" WHERE %s" |
| 219468 | ")", |
| 219469 | zSel, zDb1, zTbl, zDb2, zTbl, zExpr |
| 219470 | ); |
| 219471 | return zRet; |
| 219472 | } |
| 219473 | |
| 219474 | static int sessionDiffFindNew( |
| @@ -218475,11 +219478,13 @@ | |
| 219478 | const char *zDb1, |
| 219479 | const char *zDb2, |
| 219480 | char *zExpr |
| 219481 | ){ |
| 219482 | int rc = SQLITE_OK; |
| 219483 | char *zStmt = sessionSelectFindNew( |
| 219484 | zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr |
| 219485 | ); |
| 219486 | |
| 219487 | if( zStmt==0 ){ |
| 219488 | rc = SQLITE_NOMEM; |
| 219489 | }else{ |
| 219490 | sqlite3_stmt *pStmt; |
| @@ -218486,20 +219491,43 @@ | |
| 219491 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 219492 | if( rc==SQLITE_OK ){ |
| 219493 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 219494 | pDiffCtx->pStmt = pStmt; |
| 219495 | pDiffCtx->nOldOff = 0; |
| 219496 | pDiffCtx->bRowid = pTab->bRowid; |
| 219497 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 219498 | i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); |
| 219499 | sessionPreupdateOneChange(op, iRowid, pSession, pTab); |
| 219500 | } |
| 219501 | rc = sqlite3_finalize(pStmt); |
| 219502 | } |
| 219503 | sqlite3_free(zStmt); |
| 219504 | } |
| 219505 | |
| 219506 | return rc; |
| 219507 | } |
| 219508 | |
| 219509 | /* |
| 219510 | ** Return a comma-separated list of the fully-qualified (with both database |
| 219511 | ** and table name) column names from table pTab. e.g. |
| 219512 | ** |
| 219513 | ** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c" |
| 219514 | */ |
| 219515 | static char *sessionAllCols( |
| 219516 | const char *zDb, |
| 219517 | SessionTable *pTab |
| 219518 | ){ |
| 219519 | int ii; |
| 219520 | char *zRet = 0; |
| 219521 | for(ii=0; ii<pTab->nCol; ii++){ |
| 219522 | zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"", |
| 219523 | zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii] |
| 219524 | ); |
| 219525 | if( !zRet ) break; |
| 219526 | } |
| 219527 | return zRet; |
| 219528 | } |
| 219529 | |
| 219530 | static int sessionDiffFindModified( |
| 219531 | sqlite3_session *pSession, |
| 219532 | SessionTable *pTab, |
| 219533 | const char *zFrom, |
| @@ -218511,15 +219539,17 @@ | |
| 219539 | pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK |
| 219540 | ); |
| 219541 | if( zExpr2==0 ){ |
| 219542 | rc = SQLITE_NOMEM; |
| 219543 | }else{ |
| 219544 | char *z1 = sessionAllCols(pSession->zDb, pTab); |
| 219545 | char *z2 = sessionAllCols(zFrom, pTab); |
| 219546 | char *zStmt = sqlite3_mprintf( |
| 219547 | "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)", |
| 219548 | z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2 |
| 219549 | ); |
| 219550 | if( zStmt==0 || z1==0 || z2==0 ){ |
| 219551 | rc = SQLITE_NOMEM; |
| 219552 | }else{ |
| 219553 | sqlite3_stmt *pStmt; |
| 219554 | rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); |
| 219555 | |
| @@ -218526,16 +219556,19 @@ | |
| 219556 | if( rc==SQLITE_OK ){ |
| 219557 | SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; |
| 219558 | pDiffCtx->pStmt = pStmt; |
| 219559 | pDiffCtx->nOldOff = pTab->nCol; |
| 219560 | while( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 219561 | i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0); |
| 219562 | sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab); |
| 219563 | } |
| 219564 | rc = sqlite3_finalize(pStmt); |
| 219565 | } |
| 219566 | } |
| 219567 | sqlite3_free(zStmt); |
| 219568 | sqlite3_free(z1); |
| 219569 | sqlite3_free(z2); |
| 219570 | } |
| 219571 | |
| 219572 | return rc; |
| 219573 | } |
| 219574 | |
| @@ -218570,13 +219603,16 @@ | |
| 219603 | /* Check the table schemas match */ |
| 219604 | if( rc==SQLITE_OK ){ |
| 219605 | int bHasPk = 0; |
| 219606 | int bMismatch = 0; |
| 219607 | int nCol; /* Columns in zFrom.zTbl */ |
| 219608 | int bRowid = 0; |
| 219609 | u8 *abPK; |
| 219610 | const char **azCol = 0; |
| 219611 | rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK, |
| 219612 | pSession->bImplicitPK ? &bRowid : 0 |
| 219613 | ); |
| 219614 | if( rc==SQLITE_OK ){ |
| 219615 | if( pTo->nCol!=nCol ){ |
| 219616 | bMismatch = 1; |
| 219617 | }else{ |
| 219618 | int i; |
| @@ -219121,11 +220157,11 @@ | |
| 220157 | } |
| 220158 | |
| 220159 | /* If at least one field has been modified, this is not a no-op. */ |
| 220160 | if( bChanged ) bNoop = 0; |
| 220161 | |
| 220162 | /* Add a field to the old.* record. This is omitted if this module is |
| 220163 | ** currently generating a patchset. */ |
| 220164 | if( bPatchset==0 ){ |
| 220165 | if( bChanged || abPK[i] ){ |
| 220166 | sessionAppendBlob(pBuf, pCsr, nAdvance, &rc); |
| 220167 | }else{ |
| @@ -219223,19 +220259,20 @@ | |
| 220259 | static int sessionSelectStmt( |
| 220260 | sqlite3 *db, /* Database handle */ |
| 220261 | int bIgnoreNoop, |
| 220262 | const char *zDb, /* Database name */ |
| 220263 | const char *zTab, /* Table name */ |
| 220264 | int bRowid, |
| 220265 | int nCol, /* Number of columns in table */ |
| 220266 | const char **azCol, /* Names of table columns */ |
| 220267 | u8 *abPK, /* PRIMARY KEY array */ |
| 220268 | sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */ |
| 220269 | ){ |
| 220270 | int rc = SQLITE_OK; |
| 220271 | char *zSql = 0; |
| 220272 | const char *zSep = ""; |
| 220273 | const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*"; |
| 220274 | int nSql = -1; |
| 220275 | int i; |
| 220276 | |
| 220277 | SessionBuffer nooptest = {0, 0, 0}; |
| 220278 | SessionBuffer pkfield = {0, 0, 0}; |
| @@ -219250,11 +220287,10 @@ | |
| 220287 | "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc |
| 220288 | ); |
| 220289 | zCols = "tbl, ?2, stat"; |
| 220290 | }else{ |
| 220291 | for(i=0; i<nCol; i++){ |
| 220292 | if( abPK[i] ){ |
| 220293 | sessionAppendStr(&pkfield, zSep, &rc); |
| 220294 | sessionAppendStr(&pkvar, zSep, &rc); |
| 220295 | zSep = ", "; |
| 220296 | sessionAppendIdent(&pkfield, azCol[i], &rc); |
| @@ -219457,24 +220493,32 @@ | |
| 220493 | const char **azCol = 0; /* Table columns */ |
| 220494 | int i; /* Used to iterate through hash buckets */ |
| 220495 | sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ |
| 220496 | int nRewind = buf.nBuf; /* Initial size of write buffer */ |
| 220497 | int nNoop; /* Size of buffer after writing tbl header */ |
| 220498 | int bRowid = 0; |
| 220499 | |
| 220500 | /* Check the table schema is still Ok. */ |
| 220501 | rc = sessionTableInfo( |
| 220502 | 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK, |
| 220503 | (pSession->bImplicitPK ? &bRowid : 0) |
| 220504 | ); |
| 220505 | if( rc==SQLITE_OK && ( |
| 220506 | pTab->nCol!=nCol |
| 220507 | || pTab->bRowid!=bRowid |
| 220508 | || memcmp(abPK, pTab->abPK, nCol) |
| 220509 | )){ |
| 220510 | rc = SQLITE_SCHEMA; |
| 220511 | } |
| 220512 | |
| 220513 | /* Write a table header */ |
| 220514 | sessionAppendTableHdr(&buf, bPatchset, pTab, &rc); |
| 220515 | |
| 220516 | /* Build and compile a statement to execute: */ |
| 220517 | if( rc==SQLITE_OK ){ |
| 220518 | rc = sessionSelectStmt( |
| 220519 | db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel |
| 220520 | ); |
| 220521 | } |
| 220522 | |
| 220523 | nNoop = buf.nBuf; |
| 220524 | for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){ |
| @@ -219554,11 +220598,11 @@ | |
| 220598 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 220599 | ){ |
| 220600 | int rc; |
| 220601 | |
| 220602 | if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE; |
| 220603 | rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset); |
| 220604 | assert( rc || pnChangeset==0 |
| 220605 | || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize |
| 220606 | ); |
| 220607 | return rc; |
| 220608 | } |
| @@ -219671,10 +220715,23 @@ | |
| 220715 | } |
| 220716 | } |
| 220717 | *(int*)pArg = pSession->bEnableSize; |
| 220718 | break; |
| 220719 | } |
| 220720 | |
| 220721 | case SQLITE_SESSION_OBJCONFIG_ROWID: { |
| 220722 | int iArg = *(int*)pArg; |
| 220723 | if( iArg>=0 ){ |
| 220724 | if( pSession->pTable ){ |
| 220725 | rc = SQLITE_MISUSE; |
| 220726 | }else{ |
| 220727 | pSession->bImplicitPK = (iArg!=0); |
| 220728 | } |
| 220729 | } |
| 220730 | *(int*)pArg = pSession->bImplicitPK; |
| 220731 | break; |
| 220732 | } |
| 220733 | |
| 220734 | default: |
| 220735 | rc = SQLITE_MISUSE; |
| 220736 | } |
| 220737 | |
| @@ -220661,10 +221718,11 @@ | |
| 221718 | SessionBuffer constraints; /* Deferred constraints are stored here */ |
| 221719 | SessionBuffer rebase; /* Rebase information (if any) here */ |
| 221720 | u8 bRebaseStarted; /* If table header is already in rebase */ |
| 221721 | u8 bRebase; /* True to collect rebase information */ |
| 221722 | u8 bIgnoreNoop; /* True to ignore no-op conflicts */ |
| 221723 | int bRowid; |
| 221724 | }; |
| 221725 | |
| 221726 | /* Number of prepared UPDATE statements to cache. */ |
| 221727 | #define SESSION_UPDATE_CACHE_SZ 12 |
| 221728 | |
| @@ -220911,12 +221969,13 @@ | |
| 221969 | static int sessionSelectRow( |
| 221970 | sqlite3 *db, /* Database handle */ |
| 221971 | const char *zTab, /* Table name */ |
| 221972 | SessionApplyCtx *p /* Session changeset-apply context */ |
| 221973 | ){ |
| 221974 | /* TODO */ |
| 221975 | return sessionSelectStmt(db, p->bIgnoreNoop, |
| 221976 | "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect |
| 221977 | ); |
| 221978 | } |
| 221979 | |
| 221980 | /* |
| 221981 | ** Formulate and prepare an INSERT statement to add a record to table zTab. |
| @@ -221608,10 +222667,11 @@ | |
| 222667 | sApply.azCol = 0; |
| 222668 | sApply.abPK = 0; |
| 222669 | sApply.bStat1 = 0; |
| 222670 | sApply.bDeferConstraints = 1; |
| 222671 | sApply.bRebaseStarted = 0; |
| 222672 | sApply.bRowid = 0; |
| 222673 | memset(&sApply.constraints, 0, sizeof(SessionBuffer)); |
| 222674 | |
| 222675 | /* If an xFilter() callback was specified, invoke it now. If the |
| 222676 | ** xFilter callback returns zero, skip this table. If it returns |
| 222677 | ** non-zero, proceed. */ |
| @@ -221627,12 +222687,12 @@ | |
| 222687 | }else{ |
| 222688 | int nMinCol = 0; |
| 222689 | int i; |
| 222690 | |
| 222691 | sqlite3changeset_pk(pIter, &abPK, 0); |
| 222692 | rc = sessionTableInfo(0, db, "main", zNew, |
| 222693 | &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid |
| 222694 | ); |
| 222695 | if( rc!=SQLITE_OK ) break; |
| 222696 | for(i=0; i<sApply.nCol; i++){ |
| 222697 | if( sApply.abPK[i] ) nMinCol = i+1; |
| 222698 | } |
| @@ -223510,29 +224570,34 @@ | |
| 224570 | fts5_tokenizer *pTokApi; |
| 224571 | int bLock; /* True when table is preparing statement */ |
| 224572 | int ePattern; /* FTS_PATTERN_XXX constant */ |
| 224573 | |
| 224574 | /* Values loaded from the %_config table */ |
| 224575 | int iVersion; /* fts5 file format 'version' */ |
| 224576 | int iCookie; /* Incremented when %_config is modified */ |
| 224577 | int pgsz; /* Approximate page size used in %_data */ |
| 224578 | int nAutomerge; /* 'automerge' setting */ |
| 224579 | int nCrisisMerge; /* Maximum allowed segments per level */ |
| 224580 | int nUsermerge; /* 'usermerge' setting */ |
| 224581 | int nHashSize; /* Bytes of memory for in-memory hash */ |
| 224582 | char *zRank; /* Name of rank function */ |
| 224583 | char *zRankArgs; /* Arguments to rank function */ |
| 224584 | int bSecureDelete; /* 'secure-delete' */ |
| 224585 | |
| 224586 | /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ |
| 224587 | char **pzErrmsg; |
| 224588 | |
| 224589 | #ifdef SQLITE_DEBUG |
| 224590 | int bPrefixIndex; /* True to use prefix-indexes */ |
| 224591 | #endif |
| 224592 | }; |
| 224593 | |
| 224594 | /* Current expected value of %_config table 'version' field. And |
| 224595 | ** the expected version if the 'secure-delete' option has ever been |
| 224596 | ** set on the table. */ |
| 224597 | #define FTS5_CURRENT_VERSION 4 |
| 224598 | #define FTS5_CURRENT_VERSION_SECUREDELETE 5 |
| 224599 | |
| 224600 | #define FTS5_CONTENT_NORMAL 0 |
| 224601 | #define FTS5_CONTENT_NONE 1 |
| 224602 | #define FTS5_CONTENT_EXTERNAL 2 |
| 224603 | |
| @@ -223694,10 +224759,11 @@ | |
| 224759 | /* The following are used internally by the fts5_index.c module. They are |
| 224760 | ** defined here only to make it easier to avoid clashes with the flags |
| 224761 | ** above. */ |
| 224762 | #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 |
| 224763 | #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 |
| 224764 | #define FTS5INDEX_QUERY_SKIPHASH 0x0040 |
| 224765 | |
| 224766 | /* |
| 224767 | ** Create/destroy an Fts5Index object. |
| 224768 | */ |
| 224769 | static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); |
| @@ -223848,11 +224914,11 @@ | |
| 224914 | static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v); |
| 224915 | static int sqlite3Fts5GetVarintLen(u32 iVal); |
| 224916 | static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); |
| 224917 | static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); |
| 224918 | |
| 224919 | #define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b)) |
| 224920 | #define fts5GetVarint sqlite3Fts5GetVarint |
| 224921 | |
| 224922 | #define fts5FastGetVarint32(a, iOff, nVal) { \ |
| 224923 | nVal = (a)[iOff++]; \ |
| 224924 | if( nVal & 0x80 ){ \ |
| @@ -227340,10 +228406,11 @@ | |
| 228406 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 228407 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| 228408 | rc = SQLITE_ERROR; |
| 228409 | } |
| 228410 | |
| 228411 | assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK ); |
| 228412 | for(i=3; rc==SQLITE_OK && i<nArg; i++){ |
| 228413 | const char *zOrig = azArg[i]; |
| 228414 | const char *z; |
| 228415 | char *zOne = 0; |
| 228416 | char *zTwo = 0; |
| @@ -227693,10 +228760,22 @@ | |
| 228760 | pConfig->zRankArgs = zRankArgs; |
| 228761 | }else if( rc==SQLITE_ERROR ){ |
| 228762 | rc = SQLITE_OK; |
| 228763 | *pbBadkey = 1; |
| 228764 | } |
| 228765 | } |
| 228766 | |
| 228767 | else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){ |
| 228768 | int bVal = -1; |
| 228769 | if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ |
| 228770 | bVal = sqlite3_value_int(pVal); |
| 228771 | } |
| 228772 | if( bVal<0 ){ |
| 228773 | *pbBadkey = 1; |
| 228774 | }else{ |
| 228775 | pConfig->bSecureDelete = (bVal ? 1 : 0); |
| 228776 | } |
| 228777 | }else{ |
| 228778 | *pbBadkey = 1; |
| 228779 | } |
| 228780 | return rc; |
| 228781 | } |
| @@ -227737,19 +228816,24 @@ | |
| 228816 | } |
| 228817 | } |
| 228818 | rc = sqlite3_finalize(p); |
| 228819 | } |
| 228820 | |
| 228821 | if( rc==SQLITE_OK |
| 228822 | && iVersion!=FTS5_CURRENT_VERSION |
| 228823 | && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE |
| 228824 | ){ |
| 228825 | rc = SQLITE_ERROR; |
| 228826 | if( pConfig->pzErrmsg ){ |
| 228827 | assert( 0==*pConfig->pzErrmsg ); |
| 228828 | *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format " |
| 228829 | "(found %d, expected %d or %d) - run 'rebuild'", |
| 228830 | iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE |
| 228831 | ); |
| 228832 | } |
| 228833 | }else{ |
| 228834 | pConfig->iVersion = iVersion; |
| 228835 | } |
| 228836 | |
| 228837 | if( rc==SQLITE_OK ){ |
| 228838 | pConfig->iCookie = iCookie; |
| 228839 | } |
| @@ -227772,10 +228856,14 @@ | |
| 228856 | |
| 228857 | |
| 228858 | |
| 228859 | /* #include "fts5Int.h" */ |
| 228860 | /* #include "fts5parse.h" */ |
| 228861 | |
| 228862 | #ifndef SQLITE_FTS5_MAX_EXPR_DEPTH |
| 228863 | # define SQLITE_FTS5_MAX_EXPR_DEPTH 256 |
| 228864 | #endif |
| 228865 | |
| 228866 | /* |
| 228867 | ** All token types in the generated fts5parse.h file are greater than 0. |
| 228868 | */ |
| 228869 | #define FTS5_EOF 0 |
| @@ -227813,15 +228901,21 @@ | |
| 228901 | ** FTS5_AND (nChild, apChild valid) |
| 228902 | ** FTS5_OR (nChild, apChild valid) |
| 228903 | ** FTS5_NOT (nChild, apChild valid) |
| 228904 | ** FTS5_STRING (pNear valid) |
| 228905 | ** FTS5_TERM (pNear valid) |
| 228906 | ** |
| 228907 | ** iHeight: |
| 228908 | ** Distance from this node to furthest leaf. This is always 0 for nodes |
| 228909 | ** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one |
| 228910 | ** greater than the largest child value. |
| 228911 | */ |
| 228912 | struct Fts5ExprNode { |
| 228913 | int eType; /* Node type */ |
| 228914 | int bEof; /* True at EOF */ |
| 228915 | int bNomatch; /* True if entry is not a match */ |
| 228916 | int iHeight; /* Distance to tree leaf nodes */ |
| 228917 | |
| 228918 | /* Next method for this node. */ |
| 228919 | int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64); |
| 228920 | |
| 228921 | i64 iRowid; /* Current rowid */ |
| @@ -227887,10 +228981,35 @@ | |
| 228981 | Fts5ExprPhrase **apPhrase; /* Array of all phrases */ |
| 228982 | Fts5ExprNode *pExpr; /* Result of a successful parse */ |
| 228983 | int bPhraseToAnd; /* Convert "a+b" to "a AND b" */ |
| 228984 | }; |
| 228985 | |
| 228986 | /* |
| 228987 | ** Check that the Fts5ExprNode.iHeight variables are set correctly in |
| 228988 | ** the expression tree passed as the only argument. |
| 228989 | */ |
| 228990 | #ifndef NDEBUG |
| 228991 | static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){ |
| 228992 | if( rc==SQLITE_OK ){ |
| 228993 | if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){ |
| 228994 | assert( p->iHeight==0 ); |
| 228995 | }else{ |
| 228996 | int ii; |
| 228997 | int iMaxChild = 0; |
| 228998 | for(ii=0; ii<p->nChild; ii++){ |
| 228999 | Fts5ExprNode *pChild = p->apChild[ii]; |
| 229000 | iMaxChild = MAX(iMaxChild, pChild->iHeight); |
| 229001 | assert_expr_depth_ok(SQLITE_OK, pChild); |
| 229002 | } |
| 229003 | assert( p->iHeight==iMaxChild+1 ); |
| 229004 | } |
| 229005 | } |
| 229006 | } |
| 229007 | #else |
| 229008 | # define assert_expr_depth_ok(rc, p) |
| 229009 | #endif |
| 229010 | |
| 229011 | static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ |
| 229012 | va_list ap; |
| 229013 | va_start(ap, zFmt); |
| 229014 | if( pParse->rc==SQLITE_OK ){ |
| 229015 | assert( pParse->zErr==0 ); |
| @@ -228000,10 +229119,12 @@ | |
| 229119 | do { |
| 229120 | t = fts5ExprGetToken(&sParse, &z, &token); |
| 229121 | sqlite3Fts5Parser(pEngine, t, token, &sParse); |
| 229122 | }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); |
| 229123 | sqlite3Fts5ParserFree(pEngine, fts5ParseFree); |
| 229124 | |
| 229125 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 229126 | |
| 229127 | /* If the LHS of the MATCH expression was a user column, apply the |
| 229128 | ** implicit column-filter. */ |
| 229129 | if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){ |
| 229130 | int n = sizeof(Fts5Colset); |
| @@ -228163,11 +229284,11 @@ | |
| 229284 | |
| 229285 | static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ |
| 229286 | Fts5Parse sParse; |
| 229287 | memset(&sParse, 0, sizeof(sParse)); |
| 229288 | |
| 229289 | if( *pp1 && p2 ){ |
| 229290 | Fts5Expr *p1 = *pp1; |
| 229291 | int nPhrase = p1->nPhrase + p2->nPhrase; |
| 229292 | |
| 229293 | p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0); |
| 229294 | p2->pRoot = 0; |
| @@ -228188,11 +229309,11 @@ | |
| 229309 | p1->apExprPhrase = ap; |
| 229310 | } |
| 229311 | } |
| 229312 | sqlite3_free(p2->apExprPhrase); |
| 229313 | sqlite3_free(p2); |
| 229314 | }else if( p2 ){ |
| 229315 | *pp1 = p2; |
| 229316 | } |
| 229317 | |
| 229318 | return sParse.rc; |
| 229319 | } |
| @@ -229962,18 +231083,22 @@ | |
| 231083 | }; |
| 231084 | } |
| 231085 | } |
| 231086 | |
| 231087 | static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){ |
| 231088 | int ii = p->nChild; |
| 231089 | if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){ |
| 231090 | int nByte = sizeof(Fts5ExprNode*) * pSub->nChild; |
| 231091 | memcpy(&p->apChild[p->nChild], pSub->apChild, nByte); |
| 231092 | p->nChild += pSub->nChild; |
| 231093 | sqlite3_free(pSub); |
| 231094 | }else{ |
| 231095 | p->apChild[p->nChild++] = pSub; |
| 231096 | } |
| 231097 | for( ; ii<p->nChild; ii++){ |
| 231098 | p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1); |
| 231099 | } |
| 231100 | } |
| 231101 | |
| 231102 | /* |
| 231103 | ** This function is used when parsing LIKE or GLOB patterns against |
| 231104 | ** trigram indexes that specify either detail=column or detail=none. |
| @@ -230000,10 +231125,11 @@ | |
| 231125 | nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 231126 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 231127 | if( pRet ){ |
| 231128 | pRet->eType = FTS5_AND; |
| 231129 | pRet->nChild = nTerm; |
| 231130 | pRet->iHeight = 1; |
| 231131 | fts5ExprAssignXNext(pRet); |
| 231132 | pParse->nPhrase--; |
| 231133 | for(ii=0; ii<nTerm; ii++){ |
| 231134 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 231135 | &pParse->rc, sizeof(Fts5ExprPhrase) |
| @@ -230105,10 +231231,18 @@ | |
| 231231 | } |
| 231232 | } |
| 231233 | }else{ |
| 231234 | fts5ExprAddChildren(pRet, pLeft); |
| 231235 | fts5ExprAddChildren(pRet, pRight); |
| 231236 | if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){ |
| 231237 | sqlite3Fts5ParseError(pParse, |
| 231238 | "fts5 expression tree is too large (maximum depth %d)", |
| 231239 | SQLITE_FTS5_MAX_EXPR_DEPTH |
| 231240 | ); |
| 231241 | sqlite3_free(pRet); |
| 231242 | pRet = 0; |
| 231243 | } |
| 231244 | } |
| 231245 | } |
| 231246 | } |
| 231247 | } |
| 231248 | |
| @@ -231705,10 +232839,12 @@ | |
| 232839 | sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ |
| 232840 | sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ |
| 232841 | sqlite3_stmt *pIdxSelect; |
| 232842 | int nRead; /* Total number of blocks read */ |
| 232843 | |
| 232844 | sqlite3_stmt *pDeleteFromIdx; |
| 232845 | |
| 232846 | sqlite3_stmt *pDataVersion; |
| 232847 | i64 iStructVersion; /* data_version when pStruct read */ |
| 232848 | Fts5Structure *pStruct; /* Current db structure (or NULL) */ |
| 232849 | }; |
| 232850 | |
| @@ -231797,13 +232933,10 @@ | |
| 232933 | ** Current leaf page number within segment. |
| 232934 | ** |
| 232935 | ** iLeafOffset: |
| 232936 | ** Byte offset within the current leaf that is the first byte of the |
| 232937 | ** position list data (one byte passed the position-list size field). |
| 232938 | ** |
| 232939 | ** pLeaf: |
| 232940 | ** Buffer containing current leaf page data. Set to NULL at EOF. |
| 232941 | ** |
| 232942 | ** iTermLeafPgno, iTermLeafOffset: |
| @@ -232358,10 +233491,11 @@ | |
| 233491 | Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; |
| 233492 | if( i>=nData ){ |
| 233493 | rc = FTS5_CORRUPT; |
| 233494 | break; |
| 233495 | } |
| 233496 | assert( pSeg!=0 ); |
| 233497 | i += fts5GetVarint32(&pData[i], pSeg->iSegid); |
| 233498 | i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); |
| 233499 | i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); |
| 233500 | if( pSeg->pgnoLast<pSeg->pgnoFirst ){ |
| 233501 | rc = FTS5_CORRUPT; |
| @@ -232388,10 +233522,11 @@ | |
| 233522 | ** Add a level to the Fts5Structure.aLevel[] array of structure object |
| 233523 | ** (*ppStruct). |
| 233524 | */ |
| 233525 | static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ |
| 233526 | fts5StructureMakeWritable(pRc, ppStruct); |
| 233527 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 233528 | if( *pRc==SQLITE_OK ){ |
| 233529 | Fts5Structure *pStruct = *ppStruct; |
| 233530 | int nLevel = pStruct->nLevel; |
| 233531 | sqlite3_int64 nByte = ( |
| 233532 | sizeof(Fts5Structure) + /* Main structure */ |
| @@ -232846,46 +233981,29 @@ | |
| 233981 | assert( pLvl->bEof==0 ); |
| 233982 | if( iOff<=pLvl->iFirstOff ){ |
| 233983 | pLvl->bEof = 1; |
| 233984 | }else{ |
| 233985 | u8 *a = pLvl->pData->p; |
| 233986 | |
| 233987 | pLvl->iOff = 0; |
| 233988 | fts5DlidxLvlNext(pLvl); |
| 233989 | while( 1 ){ |
| 233990 | int nZero = 0; |
| 233991 | int ii = pLvl->iOff; |
| 233992 | u64 delta = 0; |
| 233993 | |
| 233994 | while( a[ii]==0 ){ |
| 233995 | nZero++; |
| 233996 | ii++; |
| 233997 | } |
| 233998 | ii += sqlite3Fts5GetVarint(&a[ii], &delta); |
| 233999 | |
| 234000 | if( ii>=iOff ) break; |
| 234001 | pLvl->iLeafPgno += nZero+1; |
| 234002 | pLvl->iRowid += delta; |
| 234003 | pLvl->iOff = ii; |
| 234004 | } |
| 234005 | } |
| 234006 | |
| 234007 | return pLvl->bEof; |
| 234008 | } |
| 234009 | |
| @@ -233077,11 +234195,11 @@ | |
| 234195 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 234196 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| 234197 | i64 iOff = pIter->iLeafOffset; |
| 234198 | |
| 234199 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 234200 | while( iOff>=pIter->pLeaf->szLeaf ){ |
| 234201 | fts5SegIterNextPage(p, pIter); |
| 234202 | if( pIter->pLeaf==0 ){ |
| 234203 | if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; |
| 234204 | return; |
| 234205 | } |
| @@ -233176,14 +234294,16 @@ | |
| 234294 | if( p->rc==SQLITE_OK ){ |
| 234295 | memset(pIter, 0, sizeof(*pIter)); |
| 234296 | fts5SegIterSetNext(p, pIter); |
| 234297 | pIter->pSeg = pSeg; |
| 234298 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 234299 | do { |
| 234300 | fts5SegIterNextPage(p, pIter); |
| 234301 | }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 ); |
| 234302 | } |
| 234303 | |
| 234304 | if( p->rc==SQLITE_OK && pIter->pLeaf ){ |
| 234305 | pIter->iLeafOffset = 4; |
| 234306 | assert( pIter->pLeaf!=0 ); |
| 234307 | assert_nc( pIter->pLeaf->nn>4 ); |
| 234308 | assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); |
| 234309 | pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; |
| @@ -233373,11 +234493,11 @@ | |
| 234493 | |
| 234494 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 234495 | iOff = pIter->iLeafOffset; |
| 234496 | |
| 234497 | /* Next entry is on the next page */ |
| 234498 | while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ |
| 234499 | fts5SegIterNextPage(p, pIter); |
| 234500 | if( p->rc || pIter->pLeaf==0 ) return; |
| 234501 | pIter->iRowid = 0; |
| 234502 | iOff = 4; |
| 234503 | } |
| @@ -233566,11 +234686,11 @@ | |
| 234686 | static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){ |
| 234687 | Fts5DlidxIter *pDlidx = pIter->pDlidx; |
| 234688 | Fts5Data *pLast = 0; |
| 234689 | int pgnoLast = 0; |
| 234690 | |
| 234691 | if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){ |
| 234692 | int iSegid = pIter->pSeg->iSegid; |
| 234693 | pgnoLast = fts5DlidxIterPgno(pDlidx); |
| 234694 | pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast)); |
| 234695 | }else{ |
| 234696 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| @@ -234127,11 +235247,12 @@ | |
| 235247 | return 0; |
| 235248 | } |
| 235249 | |
| 235250 | /* |
| 235251 | ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. |
| 235252 | ** It is an error if leaf iLeafPgno does not exist. Unless the db is |
| 235253 | ** a 'secure-delete' db, if it contains no rowids then this is also an error. |
| 235254 | */ |
| 235255 | static void fts5SegIterGotoPage( |
| 235256 | Fts5Index *p, /* FTS5 backend object */ |
| 235257 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 235258 | int iLeafPgno |
| @@ -234142,25 +235263,27 @@ | |
| 235263 | p->rc = FTS5_CORRUPT; |
| 235264 | }else{ |
| 235265 | fts5DataRelease(pIter->pNextLeaf); |
| 235266 | pIter->pNextLeaf = 0; |
| 235267 | pIter->iLeafPgno = iLeafPgno-1; |
| 235268 | |
| 235269 | while( p->rc==SQLITE_OK ){ |
| 235270 | int iOff; |
| 235271 | fts5SegIterNextPage(p, pIter); |
| 235272 | if( pIter->pLeaf==0 ) break; |
| 235273 | iOff = fts5LeafFirstRowidOff(pIter->pLeaf); |
| 235274 | if( iOff>0 ){ |
| 235275 | u8 *a = pIter->pLeaf->p; |
| 235276 | int n = pIter->pLeaf->szLeaf; |
| 235277 | if( iOff<4 || iOff>=n ){ |
| 235278 | p->rc = FTS5_CORRUPT; |
| 235279 | }else{ |
| 235280 | iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid); |
| 235281 | pIter->iLeafOffset = iOff; |
| 235282 | fts5SegIterLoadNPos(p, pIter); |
| 235283 | } |
| 235284 | break; |
| 235285 | } |
| 235286 | } |
| 235287 | } |
| 235288 | } |
| 235289 | |
| @@ -234871,11 +235994,11 @@ | |
| 235994 | /* Allocate space for the new multi-seg-iterator. */ |
| 235995 | if( p->rc==SQLITE_OK ){ |
| 235996 | if( iLevel<0 ){ |
| 235997 | assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) ); |
| 235998 | nSeg = pStruct->nSegment; |
| 235999 | nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH)); |
| 236000 | }else{ |
| 236001 | nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); |
| 236002 | } |
| 236003 | } |
| 236004 | *ppOut = pNew = fts5MultiIterAlloc(p, nSeg); |
| @@ -234892,11 +236015,11 @@ | |
| 236015 | |
| 236016 | /* Initialize each of the component segment iterators. */ |
| 236017 | if( p->rc==SQLITE_OK ){ |
| 236018 | if( iLevel<0 ){ |
| 236019 | Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; |
| 236020 | if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){ |
| 236021 | /* Add a segment iterator for the current contents of the hash table. */ |
| 236022 | Fts5SegIter *pIter = &pNew->aSeg[iIter++]; |
| 236023 | fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter); |
| 236024 | } |
| 236025 | for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){ |
| @@ -235647,11 +236770,11 @@ | |
| 236770 | fts5BufferZero(&buf); |
| 236771 | fts5BufferGrow(&p->rc, &buf, pData->nn); |
| 236772 | fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); |
| 236773 | fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); |
| 236774 | fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); |
| 236775 | fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]); |
| 236776 | if( p->rc==SQLITE_OK ){ |
| 236777 | /* Set the szLeaf field */ |
| 236778 | fts5PutU16(&buf.p[2], (u16)buf.n); |
| 236779 | } |
| 236780 | |
| @@ -235925,20 +237048,20 @@ | |
| 237048 | Fts5Index *p, /* FTS5 backend object */ |
| 237049 | Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ |
| 237050 | ){ |
| 237051 | const int nCrisis = p->pConfig->nCrisisMerge; |
| 237052 | Fts5Structure *pStruct = *ppStruct; |
| 237053 | if( pStruct && pStruct->nLevel>0 ){ |
| 237054 | int iLvl = 0; |
| 237055 | while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){ |
| 237056 | fts5IndexMergeLevel(p, &pStruct, iLvl, 0); |
| 237057 | assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) ); |
| 237058 | fts5StructurePromote(p, iLvl+1, pStruct); |
| 237059 | iLvl++; |
| 237060 | } |
| 237061 | *ppStruct = pStruct; |
| 237062 | } |
| 237063 | } |
| 237064 | |
| 237065 | static int fts5IndexReturn(Fts5Index *p){ |
| 237066 | int rc = p->rc; |
| 237067 | p->rc = SQLITE_OK; |
| @@ -235967,10 +237090,417 @@ | |
| 237090 | ret += i; |
| 237091 | } |
| 237092 | } |
| 237093 | return ret; |
| 237094 | } |
| 237095 | |
| 237096 | /* |
| 237097 | ** Execute the SQL statement: |
| 237098 | ** |
| 237099 | ** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno); |
| 237100 | ** |
| 237101 | ** This is used when a secure-delete operation removes the last term |
| 237102 | ** from a segment leaf page. In that case the %_idx entry is removed |
| 237103 | ** too. This is done to ensure that if all instances of a token are |
| 237104 | ** removed from an fts5 database in secure-delete mode, no trace of |
| 237105 | ** the token itself remains in the database. |
| 237106 | */ |
| 237107 | static void fts5SecureDeleteIdxEntry( |
| 237108 | Fts5Index *p, /* FTS5 backend object */ |
| 237109 | int iSegid, /* Id of segment to delete entry for */ |
| 237110 | int iPgno /* Page number within segment */ |
| 237111 | ){ |
| 237112 | if( iPgno!=1 ){ |
| 237113 | assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE ); |
| 237114 | if( p->pDeleteFromIdx==0 ){ |
| 237115 | fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf( |
| 237116 | "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)", |
| 237117 | p->pConfig->zDb, p->pConfig->zName |
| 237118 | )); |
| 237119 | } |
| 237120 | if( p->rc==SQLITE_OK ){ |
| 237121 | sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid); |
| 237122 | sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno); |
| 237123 | sqlite3_step(p->pDeleteFromIdx); |
| 237124 | p->rc = sqlite3_reset(p->pDeleteFromIdx); |
| 237125 | } |
| 237126 | } |
| 237127 | } |
| 237128 | |
| 237129 | /* |
| 237130 | ** This is called when a secure-delete operation removes a position-list |
| 237131 | ** that overflows onto segment page iPgno of segment pSeg. This function |
| 237132 | ** rewrites node iPgno, and possibly one or more of its right-hand peers, |
| 237133 | ** to remove this portion of the position list. |
| 237134 | ** |
| 237135 | ** Output variable (*pbLastInDoclist) is set to true if the position-list |
| 237136 | ** removed is followed by a new term or the end-of-segment, or false if |
| 237137 | ** it is followed by another rowid/position list. |
| 237138 | */ |
| 237139 | static void fts5SecureDeleteOverflow( |
| 237140 | Fts5Index *p, |
| 237141 | Fts5StructureSegment *pSeg, |
| 237142 | int iPgno, |
| 237143 | int *pbLastInDoclist |
| 237144 | ){ |
| 237145 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); |
| 237146 | int pgno; |
| 237147 | Fts5Data *pLeaf = 0; |
| 237148 | assert( iPgno!=1 ); |
| 237149 | |
| 237150 | *pbLastInDoclist = 1; |
| 237151 | for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){ |
| 237152 | i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno); |
| 237153 | int iNext = 0; |
| 237154 | u8 *aPg = 0; |
| 237155 | |
| 237156 | pLeaf = fts5DataRead(p, iRowid); |
| 237157 | if( pLeaf==0 ) break; |
| 237158 | aPg = pLeaf->p; |
| 237159 | |
| 237160 | iNext = fts5GetU16(&aPg[0]); |
| 237161 | if( iNext!=0 ){ |
| 237162 | *pbLastInDoclist = 0; |
| 237163 | } |
| 237164 | if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ |
| 237165 | fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); |
| 237166 | } |
| 237167 | |
| 237168 | if( iNext==0 ){ |
| 237169 | /* The page contains no terms or rowids. Replace it with an empty |
| 237170 | ** page and move on to the right-hand peer. */ |
| 237171 | const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04}; |
| 237172 | assert_nc( bDetailNone==0 || pLeaf->nn==4 ); |
| 237173 | if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty)); |
| 237174 | fts5DataRelease(pLeaf); |
| 237175 | pLeaf = 0; |
| 237176 | }else if( bDetailNone ){ |
| 237177 | break; |
| 237178 | }else if( iNext>=pLeaf->szLeaf || iNext<4 ){ |
| 237179 | p->rc = FTS5_CORRUPT; |
| 237180 | break; |
| 237181 | }else{ |
| 237182 | int nShift = iNext - 4; |
| 237183 | int nPg; |
| 237184 | |
| 237185 | int nIdx = 0; |
| 237186 | u8 *aIdx = 0; |
| 237187 | |
| 237188 | /* Unless the current page footer is 0 bytes in size (in which case |
| 237189 | ** the new page footer will be as well), allocate and populate a |
| 237190 | ** buffer containing the new page footer. Set stack variables aIdx |
| 237191 | ** and nIdx accordingly. */ |
| 237192 | if( pLeaf->nn>pLeaf->szLeaf ){ |
| 237193 | int iFirst = 0; |
| 237194 | int i1 = pLeaf->szLeaf; |
| 237195 | int i2 = 0; |
| 237196 | |
| 237197 | aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2); |
| 237198 | if( aIdx==0 ) break; |
| 237199 | i1 += fts5GetVarint32(&aPg[i1], iFirst); |
| 237200 | i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift); |
| 237201 | if( i1<pLeaf->nn ){ |
| 237202 | memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1); |
| 237203 | i2 += (pLeaf->nn-i1); |
| 237204 | } |
| 237205 | nIdx = i2; |
| 237206 | } |
| 237207 | |
| 237208 | /* Modify the contents of buffer aPg[]. Set nPg to the new size |
| 237209 | ** in bytes. The new page is always smaller than the old. */ |
| 237210 | nPg = pLeaf->szLeaf - nShift; |
| 237211 | memmove(&aPg[4], &aPg[4+nShift], nPg-4); |
| 237212 | fts5PutU16(&aPg[2], nPg); |
| 237213 | if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4); |
| 237214 | if( nIdx>0 ){ |
| 237215 | memcpy(&aPg[nPg], aIdx, nIdx); |
| 237216 | nPg += nIdx; |
| 237217 | } |
| 237218 | sqlite3_free(aIdx); |
| 237219 | |
| 237220 | /* Write the new page to disk and exit the loop */ |
| 237221 | assert( nPg>4 || fts5GetU16(aPg)==0 ); |
| 237222 | fts5DataWrite(p, iRowid, aPg, nPg); |
| 237223 | break; |
| 237224 | } |
| 237225 | } |
| 237226 | fts5DataRelease(pLeaf); |
| 237227 | } |
| 237228 | |
| 237229 | /* |
| 237230 | ** Completely remove the entry that pSeg currently points to from |
| 237231 | ** the database. |
| 237232 | */ |
| 237233 | static void fts5DoSecureDelete( |
| 237234 | Fts5Index *p, |
| 237235 | Fts5SegIter *pSeg |
| 237236 | ){ |
| 237237 | const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE); |
| 237238 | int iSegid = pSeg->pSeg->iSegid; |
| 237239 | u8 *aPg = pSeg->pLeaf->p; |
| 237240 | int nPg = pSeg->pLeaf->nn; |
| 237241 | int iPgIdx = pSeg->pLeaf->szLeaf; |
| 237242 | |
| 237243 | u64 iDelta = 0; |
| 237244 | u64 iNextDelta = 0; |
| 237245 | int iNextOff = 0; |
| 237246 | int iOff = 0; |
| 237247 | int nIdx = 0; |
| 237248 | u8 *aIdx = 0; |
| 237249 | int bLastInDoclist = 0; |
| 237250 | int iIdx = 0; |
| 237251 | int iStart = 0; |
| 237252 | int iKeyOff = 0; |
| 237253 | int iPrevKeyOff = 0; |
| 237254 | int iDelKeyOff = 0; /* Offset of deleted key, if any */ |
| 237255 | |
| 237256 | nIdx = nPg-iPgIdx; |
| 237257 | aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16); |
| 237258 | if( p->rc ) return; |
| 237259 | memcpy(aIdx, &aPg[iPgIdx], nIdx); |
| 237260 | |
| 237261 | /* At this point segment iterator pSeg points to the entry |
| 237262 | ** this function should remove from the b-tree segment. |
| 237263 | ** |
| 237264 | ** In detail=full or detail=column mode, pSeg->iLeafOffset is the |
| 237265 | ** offset of the first byte in the position-list for the entry to |
| 237266 | ** remove. Immediately before this comes two varints that will also |
| 237267 | ** need to be removed: |
| 237268 | ** |
| 237269 | ** + the rowid or delta rowid value for the entry, and |
| 237270 | ** + the size of the position list in bytes. |
| 237271 | ** |
| 237272 | ** Or, in detail=none mode, there is a single varint prior to |
| 237273 | ** pSeg->iLeafOffset - the rowid or delta rowid value. |
| 237274 | ** |
| 237275 | ** This block sets the following variables: |
| 237276 | ** |
| 237277 | ** iStart: |
| 237278 | ** iDelta: |
| 237279 | */ |
| 237280 | { |
| 237281 | int iSOP; |
| 237282 | if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ |
| 237283 | iStart = pSeg->iTermLeafOffset; |
| 237284 | }else{ |
| 237285 | iStart = fts5GetU16(&aPg[0]); |
| 237286 | } |
| 237287 | |
| 237288 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 237289 | assert_nc( iSOP<=pSeg->iLeafOffset ); |
| 237290 | |
| 237291 | if( bDetailNone ){ |
| 237292 | while( iSOP<pSeg->iLeafOffset ){ |
| 237293 | if( aPg[iSOP]==0x00 ) iSOP++; |
| 237294 | if( aPg[iSOP]==0x00 ) iSOP++; |
| 237295 | iStart = iSOP; |
| 237296 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 237297 | } |
| 237298 | |
| 237299 | iNextOff = iSOP; |
| 237300 | if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; |
| 237301 | if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++; |
| 237302 | |
| 237303 | }else{ |
| 237304 | int nPos = 0; |
| 237305 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos); |
| 237306 | while( iSOP<pSeg->iLeafOffset ){ |
| 237307 | iStart = iSOP + (nPos/2); |
| 237308 | iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); |
| 237309 | iSOP += fts5GetVarint32(&aPg[iSOP], nPos); |
| 237310 | } |
| 237311 | assert_nc( iSOP==pSeg->iLeafOffset ); |
| 237312 | iNextOff = pSeg->iLeafOffset + pSeg->nPos; |
| 237313 | } |
| 237314 | } |
| 237315 | |
| 237316 | iOff = iStart; |
| 237317 | if( iNextOff>=iPgIdx ){ |
| 237318 | int pgno = pSeg->iLeafPgno+1; |
| 237319 | fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); |
| 237320 | iNextOff = iPgIdx; |
| 237321 | }else{ |
| 237322 | /* Set bLastInDoclist to true if the entry being removed is the last |
| 237323 | ** in its doclist. */ |
| 237324 | for(iIdx=0, iKeyOff=0; iIdx<nIdx; /* no-op */){ |
| 237325 | u32 iVal = 0; |
| 237326 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); |
| 237327 | iKeyOff += iVal; |
| 237328 | if( iKeyOff==iNextOff ){ |
| 237329 | bLastInDoclist = 1; |
| 237330 | } |
| 237331 | } |
| 237332 | } |
| 237333 | |
| 237334 | if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist||iNextOff==iPgIdx) ){ |
| 237335 | fts5PutU16(&aPg[0], 0); |
| 237336 | } |
| 237337 | |
| 237338 | if( bLastInDoclist==0 ){ |
| 237339 | if( iNextOff!=iPgIdx ){ |
| 237340 | iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta); |
| 237341 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); |
| 237342 | } |
| 237343 | }else if( |
| 237344 | iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno |
| 237345 | ){ |
| 237346 | /* The entry being removed was the only position list in its |
| 237347 | ** doclist. Therefore the term needs to be removed as well. */ |
| 237348 | int iKey = 0; |
| 237349 | for(iIdx=0, iKeyOff=0; iIdx<nIdx; iKey++){ |
| 237350 | u32 iVal = 0; |
| 237351 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); |
| 237352 | if( (iKeyOff+iVal)>(u32)iStart ) break; |
| 237353 | iKeyOff += iVal; |
| 237354 | } |
| 237355 | |
| 237356 | iDelKeyOff = iOff = iKeyOff; |
| 237357 | if( iNextOff!=iPgIdx ){ |
| 237358 | int nPrefix = 0; |
| 237359 | int nSuffix = 0; |
| 237360 | int nPrefix2 = 0; |
| 237361 | int nSuffix2 = 0; |
| 237362 | |
| 237363 | iDelKeyOff = iNextOff; |
| 237364 | iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); |
| 237365 | iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); |
| 237366 | |
| 237367 | if( iKey!=1 ){ |
| 237368 | iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); |
| 237369 | } |
| 237370 | iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); |
| 237371 | |
| 237372 | nPrefix = MIN(nPrefix, nPrefix2); |
| 237373 | nSuffix = (nPrefix2 + nSuffix2) - nPrefix; |
| 237374 | |
| 237375 | if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ |
| 237376 | p->rc = FTS5_CORRUPT; |
| 237377 | }else{ |
| 237378 | if( iKey!=1 ){ |
| 237379 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); |
| 237380 | } |
| 237381 | iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); |
| 237382 | if( nPrefix2>nPrefix ){ |
| 237383 | memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); |
| 237384 | iOff += (nPrefix2-nPrefix); |
| 237385 | } |
| 237386 | memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2); |
| 237387 | iOff += nSuffix2; |
| 237388 | iNextOff += nSuffix2; |
| 237389 | } |
| 237390 | } |
| 237391 | }else if( iStart==4 ){ |
| 237392 | int iPgno; |
| 237393 | |
| 237394 | assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno ); |
| 237395 | /* The entry being removed may be the only position list in |
| 237396 | ** its doclist. */ |
| 237397 | for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ |
| 237398 | Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); |
| 237399 | int bEmpty = (pPg && pPg->nn==4); |
| 237400 | fts5DataRelease(pPg); |
| 237401 | if( bEmpty==0 ) break; |
| 237402 | } |
| 237403 | |
| 237404 | if( iPgno==pSeg->iTermLeafPgno ){ |
| 237405 | i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); |
| 237406 | Fts5Data *pTerm = fts5DataRead(p, iId); |
| 237407 | if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ |
| 237408 | u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; |
| 237409 | int nTermIdx = pTerm->nn - pTerm->szLeaf; |
| 237410 | int iTermIdx = 0; |
| 237411 | int iTermOff = 0; |
| 237412 | |
| 237413 | while( 1 ){ |
| 237414 | u32 iVal = 0; |
| 237415 | int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal); |
| 237416 | iTermOff += iVal; |
| 237417 | if( (iTermIdx+nByte)>=nTermIdx ) break; |
| 237418 | iTermIdx += nByte; |
| 237419 | } |
| 237420 | nTermIdx = iTermIdx; |
| 237421 | |
| 237422 | memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); |
| 237423 | fts5PutU16(&pTerm->p[2], iTermOff); |
| 237424 | |
| 237425 | fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); |
| 237426 | if( nTermIdx==0 ){ |
| 237427 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); |
| 237428 | } |
| 237429 | } |
| 237430 | fts5DataRelease(pTerm); |
| 237431 | } |
| 237432 | } |
| 237433 | |
| 237434 | if( p->rc==SQLITE_OK ){ |
| 237435 | const int nMove = nPg - iNextOff; |
| 237436 | int nShift = 0; |
| 237437 | |
| 237438 | memmove(&aPg[iOff], &aPg[iNextOff], nMove); |
| 237439 | iPgIdx -= (iNextOff - iOff); |
| 237440 | nPg = iPgIdx; |
| 237441 | fts5PutU16(&aPg[2], iPgIdx); |
| 237442 | |
| 237443 | nShift = iNextOff - iOff; |
| 237444 | for(iIdx=0, iKeyOff=0, iPrevKeyOff=0; iIdx<nIdx; /* no-op */){ |
| 237445 | u32 iVal = 0; |
| 237446 | iIdx += fts5GetVarint32(&aIdx[iIdx], iVal); |
| 237447 | iKeyOff += iVal; |
| 237448 | if( iKeyOff!=iDelKeyOff ){ |
| 237449 | if( iKeyOff>iOff ){ |
| 237450 | iKeyOff -= nShift; |
| 237451 | nShift = 0; |
| 237452 | } |
| 237453 | nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOff - iPrevKeyOff); |
| 237454 | iPrevKeyOff = iKeyOff; |
| 237455 | } |
| 237456 | } |
| 237457 | |
| 237458 | if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){ |
| 237459 | fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno); |
| 237460 | } |
| 237461 | |
| 237462 | assert_nc( nPg>4 || fts5GetU16(aPg)==0 ); |
| 237463 | fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg,nPg); |
| 237464 | } |
| 237465 | sqlite3_free(aIdx); |
| 237466 | } |
| 237467 | |
| 237468 | /* |
| 237469 | ** This is called as part of flushing a delete to disk in 'secure-delete' |
| 237470 | ** mode. It edits the segments within the database described by argument |
| 237471 | ** pStruct to remove the entries for term zTerm, rowid iRowid. |
| 237472 | */ |
| 237473 | static void fts5FlushSecureDelete( |
| 237474 | Fts5Index *p, |
| 237475 | Fts5Structure *pStruct, |
| 237476 | const char *zTerm, |
| 237477 | i64 iRowid |
| 237478 | ){ |
| 237479 | const int f = FTS5INDEX_QUERY_SKIPHASH; |
| 237480 | int nTerm = (int)strlen(zTerm); |
| 237481 | Fts5Iter *pIter = 0; /* Used to find term instance */ |
| 237482 | |
| 237483 | fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); |
| 237484 | if( fts5MultiIterEof(p, pIter)==0 ){ |
| 237485 | i64 iThis = fts5MultiIterRowid(pIter); |
| 237486 | if( iThis<iRowid ){ |
| 237487 | fts5MultiIterNextFrom(p, pIter, iRowid); |
| 237488 | } |
| 237489 | |
| 237490 | if( p->rc==SQLITE_OK |
| 237491 | && fts5MultiIterEof(p, pIter)==0 |
| 237492 | && iRowid==fts5MultiIterRowid(pIter) |
| 237493 | ){ |
| 237494 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; |
| 237495 | fts5DoSecureDelete(p, pSeg); |
| 237496 | } |
| 237497 | } |
| 237498 | |
| 237499 | fts5MultiIterFree(pIter); |
| 237500 | } |
| 237501 | |
| 237502 | |
| 237503 | /* |
| 237504 | ** Flush the contents of in-memory hash table iHash to a new level-0 |
| 237505 | ** segment on disk. Also update the corresponding structure record. |
| 237506 | ** |
| @@ -235990,10 +237520,11 @@ | |
| 237520 | fts5StructureInvalidate(p); |
| 237521 | |
| 237522 | if( iSegid ){ |
| 237523 | const int pgsz = p->pConfig->pgsz; |
| 237524 | int eDetail = p->pConfig->eDetail; |
| 237525 | int bSecureDelete = p->pConfig->bSecureDelete; |
| 237526 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 237527 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 237528 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 237529 | |
| 237530 | Fts5SegWriter writer; |
| @@ -236012,44 +237543,81 @@ | |
| 237543 | if( p->rc==SQLITE_OK ){ |
| 237544 | p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0); |
| 237545 | } |
| 237546 | while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){ |
| 237547 | const char *zTerm; /* Buffer containing term */ |
| 237548 | int nTerm; /* Size of zTerm in bytes */ |
| 237549 | const u8 *pDoclist; /* Pointer to doclist for this term */ |
| 237550 | int nDoclist; /* Size of doclist in bytes */ |
| 237551 | |
| 237552 | /* Get the term and doclist for this entry. */ |
| 237553 | sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); |
| 237554 | nTerm = (int)strlen(zTerm); |
| 237555 | if( bSecureDelete==0 ){ |
| 237556 | fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); |
| 237557 | if( p->rc!=SQLITE_OK ) break; |
| 237558 | assert( writer.bFirstRowidInPage==0 ); |
| 237559 | } |
| 237560 | |
| 237561 | if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ |
| 237562 | /* The entire doclist will fit on the current leaf. */ |
| 237563 | fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist); |
| 237564 | }else{ |
| 237565 | int bTermWritten = !bSecureDelete; |
| 237566 | i64 iRowid = 0; |
| 237567 | i64 iPrev = 0; |
| 237568 | int iOff = 0; |
| 237569 | |
| 237570 | /* The entire doclist will not fit on this leaf. The following |
| 237571 | ** loop iterates through the poslists that make up the current |
| 237572 | ** doclist. */ |
| 237573 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 237574 | u64 iDelta = 0; |
| 237575 | iOff += fts5GetVarint(&pDoclist[iOff], &iDelta); |
| 237576 | iRowid += iDelta; |
| 237577 | |
| 237578 | /* If in secure delete mode, and if this entry in the poslist is |
| 237579 | ** in fact a delete, then edit the existing segments directly |
| 237580 | ** using fts5FlushSecureDelete(). */ |
| 237581 | if( bSecureDelete ){ |
| 237582 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 237583 | if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ |
| 237584 | fts5FlushSecureDelete(p, pStruct, zTerm, iRowid); |
| 237585 | iOff++; |
| 237586 | if( iOff<nDoclist && pDoclist[iOff]==0x00 ){ |
| 237587 | iOff++; |
| 237588 | nDoclist = 0; |
| 237589 | }else{ |
| 237590 | continue; |
| 237591 | } |
| 237592 | } |
| 237593 | }else if( (pDoclist[iOff] & 0x01) ){ |
| 237594 | fts5FlushSecureDelete(p, pStruct, zTerm, iRowid); |
| 237595 | if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ |
| 237596 | iOff++; |
| 237597 | continue; |
| 237598 | } |
| 237599 | } |
| 237600 | } |
| 237601 | |
| 237602 | if( p->rc==SQLITE_OK && bTermWritten==0 ){ |
| 237603 | fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); |
| 237604 | bTermWritten = 1; |
| 237605 | assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 ); |
| 237606 | } |
| 237607 | |
| 237608 | if( writer.bFirstRowidInPage ){ |
| 237609 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 237610 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| 237611 | writer.bFirstRowidInPage = 0; |
| 237612 | fts5WriteDlidxAppend(p, &writer, iRowid); |
| 237613 | }else{ |
| 237614 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev); |
| 237615 | } |
| 237616 | if( p->rc!=SQLITE_OK ) break; |
| 237617 | assert( pBuf->n<=pBuf->nSpace ); |
| 237618 | iPrev = iRowid; |
| 237619 | |
| 237620 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 237621 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ |
| 237622 | pBuf->p[pBuf->n++] = 0; |
| 237623 | iOff++; |
| @@ -236104,24 +237672,27 @@ | |
| 237672 | if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); |
| 237673 | } |
| 237674 | sqlite3Fts5HashClear(pHash); |
| 237675 | fts5WriteFinish(p, &writer, &pgnoLast); |
| 237676 | |
| 237677 | assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); |
| 237678 | if( pgnoLast>0 ){ |
| 237679 | /* Update the Fts5Structure. It is written back to the database by the |
| 237680 | ** fts5StructureRelease() call below. */ |
| 237681 | if( pStruct->nLevel==0 ){ |
| 237682 | fts5StructureAddLevel(&p->rc, &pStruct); |
| 237683 | } |
| 237684 | fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); |
| 237685 | if( p->rc==SQLITE_OK ){ |
| 237686 | pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; |
| 237687 | pSeg->iSegid = iSegid; |
| 237688 | pSeg->pgnoFirst = 1; |
| 237689 | pSeg->pgnoLast = pgnoLast; |
| 237690 | pStruct->nSegment++; |
| 237691 | } |
| 237692 | fts5StructurePromote(p, 0, pStruct); |
| 237693 | } |
| 237694 | } |
| 237695 | |
| 237696 | fts5IndexAutomerge(p, &pStruct, pgnoLast); |
| 237697 | fts5IndexCrisismerge(p, &pStruct); |
| 237698 | fts5StructureWrite(p, pStruct); |
| @@ -236858,10 +238429,11 @@ | |
| 238429 | sqlite3_finalize(p->pDeleter); |
| 238430 | sqlite3_finalize(p->pIdxWriter); |
| 238431 | sqlite3_finalize(p->pIdxDeleter); |
| 238432 | sqlite3_finalize(p->pIdxSelect); |
| 238433 | sqlite3_finalize(p->pDataVersion); |
| 238434 | sqlite3_finalize(p->pDeleteFromIdx); |
| 238435 | sqlite3Fts5HashFree(p->pHash); |
| 238436 | sqlite3_free(p->zDataTbl); |
| 238437 | sqlite3_free(p); |
| 238438 | } |
| 238439 | return rc; |
| @@ -237488,10 +239060,11 @@ | |
| 239060 | static void fts5IndexIntegrityCheckSegment( |
| 239061 | Fts5Index *p, /* FTS5 backend object */ |
| 239062 | Fts5StructureSegment *pSeg /* Segment to check internal consistency */ |
| 239063 | ){ |
| 239064 | Fts5Config *pConfig = p->pConfig; |
| 239065 | int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE); |
| 239066 | sqlite3_stmt *pStmt = 0; |
| 239067 | int rc2; |
| 239068 | int iIdxPrevLeaf = pSeg->pgnoFirst-1; |
| 239069 | int iDlidxPrevLeaf = pSeg->pgnoLast; |
| 239070 | |
| @@ -237523,11 +239096,23 @@ | |
| 239096 | /* Check that the leaf contains at least one term, and that it is equal |
| 239097 | ** to or larger than the split-key in zIdxTerm. Also check that if there |
| 239098 | ** is also a rowid pointer within the leaf page header, it points to a |
| 239099 | ** location before the term. */ |
| 239100 | if( pLeaf->nn<=pLeaf->szLeaf ){ |
| 239101 | |
| 239102 | if( nIdxTerm==0 |
| 239103 | && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE |
| 239104 | && pLeaf->nn==pLeaf->szLeaf |
| 239105 | && pLeaf->nn==4 |
| 239106 | ){ |
| 239107 | /* special case - the very first page in a segment keeps its %_idx |
| 239108 | ** entry even if all the terms are removed from it by secure-delete |
| 239109 | ** operations. */ |
| 239110 | }else{ |
| 239111 | p->rc = FTS5_CORRUPT; |
| 239112 | } |
| 239113 | |
| 239114 | }else{ |
| 239115 | int iOff; /* Offset of first term on leaf */ |
| 239116 | int iRowidOff; /* Offset of first rowid on leaf */ |
| 239117 | int nTerm; /* Size of term on leaf in bytes */ |
| 239118 | int res; /* Comparison of term and split-key */ |
| @@ -237587,13 +239172,16 @@ | |
| 239172 | i64 iRowid; |
| 239173 | int iRowidOff = fts5LeafFirstRowidOff(pLeaf); |
| 239174 | ASSERT_SZLEAF_OK(pLeaf); |
| 239175 | if( iRowidOff>=pLeaf->szLeaf ){ |
| 239176 | p->rc = FTS5_CORRUPT; |
| 239177 | }else if( bSecureDelete==0 || iRowidOff>0 ){ |
| 239178 | i64 iDlRowid = fts5DlidxIterRowid(pDlidx); |
| 239179 | fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); |
| 239180 | if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){ |
| 239181 | p->rc = FTS5_CORRUPT; |
| 239182 | } |
| 239183 | } |
| 239184 | fts5DataRelease(pLeaf); |
| 239185 | } |
| 239186 | } |
| 239187 | |
| @@ -239851,10 +241439,12 @@ | |
| 241439 | ){ |
| 241440 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 241441 | Fts5Config *pConfig = pTab->p.pConfig; |
| 241442 | int eType0; /* value_type() of apVal[0] */ |
| 241443 | int rc = SQLITE_OK; /* Return code */ |
| 241444 | int bUpdateOrDelete = 0; |
| 241445 | |
| 241446 | |
| 241447 | /* A transaction must be open when this is called. */ |
| 241448 | assert( pTab->ts.eState==1 || pTab->ts.eState==2 ); |
| 241449 | |
| 241450 | assert( pVtab->zErrMsg==0 ); |
| @@ -239861,10 +241451,15 @@ | |
| 241451 | assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); |
| 241452 | assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER |
| 241453 | || sqlite3_value_type(apVal[0])==SQLITE_NULL |
| 241454 | ); |
| 241455 | assert( pTab->p.pConfig->pzErrmsg==0 ); |
| 241456 | if( pConfig->pgsz==0 ){ |
| 241457 | rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); |
| 241458 | if( rc!=SQLITE_OK ) return rc; |
| 241459 | } |
| 241460 | |
| 241461 | pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; |
| 241462 | |
| 241463 | /* Put any active cursors into REQUIRE_SEEK state. */ |
| 241464 | fts5TripCursors(pTab); |
| 241465 | |
| @@ -239913,10 +241508,11 @@ | |
| 241508 | |
| 241509 | /* DELETE */ |
| 241510 | else if( nArg==1 ){ |
| 241511 | i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
| 241512 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); |
| 241513 | bUpdateOrDelete = 1; |
| 241514 | } |
| 241515 | |
| 241516 | /* INSERT or UPDATE */ |
| 241517 | else{ |
| 241518 | int eType1 = sqlite3_value_numeric_type(apVal[1]); |
| @@ -239928,10 +241524,11 @@ | |
| 241524 | else if( eType0!=SQLITE_INTEGER ){ |
| 241525 | /* If this is a REPLACE, first remove the current entry (if any) */ |
| 241526 | if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ |
| 241527 | i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ |
| 241528 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); |
| 241529 | bUpdateOrDelete = 1; |
| 241530 | } |
| 241531 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 241532 | } |
| 241533 | |
| 241534 | /* UPDATE */ |
| @@ -239956,13 +241553,27 @@ | |
| 241553 | } |
| 241554 | }else{ |
| 241555 | rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
| 241556 | fts5StorageInsert(&rc, pTab, apVal, pRowid); |
| 241557 | } |
| 241558 | bUpdateOrDelete = 1; |
| 241559 | } |
| 241560 | } |
| 241561 | } |
| 241562 | |
| 241563 | if( rc==SQLITE_OK |
| 241564 | && bUpdateOrDelete |
| 241565 | && pConfig->bSecureDelete |
| 241566 | && pConfig->iVersion==FTS5_CURRENT_VERSION |
| 241567 | ){ |
| 241568 | rc = sqlite3Fts5StorageConfigValue( |
| 241569 | pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE |
| 241570 | ); |
| 241571 | if( rc==SQLITE_OK ){ |
| 241572 | pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE; |
| 241573 | } |
| 241574 | } |
| 241575 | |
| 241576 | pTab->p.pConfig->pzErrmsg = 0; |
| 241577 | return rc; |
| 241578 | } |
| 241579 | |
| @@ -240819,10 +242430,11 @@ | |
| 242430 | static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
| 242431 | Fts5FullTable *pTab = (Fts5FullTable*)pVtab; |
| 242432 | UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
| 242433 | fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); |
| 242434 | fts5TripCursors(pTab); |
| 242435 | pTab->p.pConfig->pgsz = 0; |
| 242436 | return sqlite3Fts5StorageRollback(pTab->pStorage); |
| 242437 | } |
| 242438 | |
| 242439 | /* |
| 242440 | ** Register a new auxiliary function with global context pGlobal. |
| @@ -241021,11 +242633,11 @@ | |
| 242633 | int nArg, /* Number of args */ |
| 242634 | sqlite3_value **apUnused /* Function arguments */ |
| 242635 | ){ |
| 242636 | assert( nArg==0 ); |
| 242637 | UNUSED_PARAM2(nArg, apUnused); |
| 242638 | sqlite3_result_text(pCtx, "fts5: 2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0", -1, SQLITE_TRANSIENT); |
| 242639 | } |
| 242640 | |
| 242641 | /* |
| 242642 | ** Return true if zName is the extension on one of the shadow tables used |
| 242643 | ** by this module. |
| 242644 |
+80
-48
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.42.0" |
| 150 | 150 | #define SQLITE_VERSION_NUMBER 3042000 |
| 151 | -#define SQLITE_SOURCE_ID "2023-04-10 18:44:00 4c5a3c5fb351cc1c2ce16c33314ce19c53531f09263f87456283d9d756002f9d" | |
| 151 | +#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -2140,32 +2140,32 @@ | ||
| 2140 | 2140 | ** configuration setting is never used, then the default maximum is determined |
| 2141 | 2141 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2142 | 2142 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2143 | 2143 | ** </dl> |
| 2144 | 2144 | */ |
| 2145 | -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ | |
| 2146 | -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ | |
| 2147 | -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ | |
| 2148 | -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ | |
| 2149 | -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | |
| 2150 | -#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ | |
| 2151 | -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ | |
| 2152 | -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ | |
| 2153 | -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ | |
| 2154 | -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ | |
| 2155 | -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ | |
| 2156 | -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ | |
| 2157 | -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ | |
| 2158 | -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ | |
| 2159 | -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ | |
| 2160 | -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ | |
| 2161 | -#define SQLITE_CONFIG_URI 17 /* int */ | |
| 2162 | -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ | |
| 2163 | -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ | |
| 2145 | +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ | |
| 2146 | +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ | |
| 2147 | +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ | |
| 2148 | +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ | |
| 2149 | +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | |
| 2150 | +#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ | |
| 2151 | +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ | |
| 2152 | +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ | |
| 2153 | +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ | |
| 2154 | +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ | |
| 2155 | +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ | |
| 2156 | +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ | |
| 2157 | +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ | |
| 2158 | +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ | |
| 2159 | +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ | |
| 2160 | +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ | |
| 2161 | +#define SQLITE_CONFIG_URI 17 /* int */ | |
| 2162 | +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ | |
| 2163 | +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ | |
| 2164 | 2164 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2165 | -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ | |
| 2166 | -#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ | |
| 2165 | +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ | |
| 2166 | +#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ | |
| 2167 | 2167 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2168 | 2168 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2169 | 2169 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2170 | 2170 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2171 | 2171 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2396,29 +2396,29 @@ | ||
| 2396 | 2396 | ** additional information. This feature can also be turned on and off |
| 2397 | 2397 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2398 | 2398 | ** </dd> |
| 2399 | 2399 | ** |
| 2400 | 2400 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2401 | -** <dt>SQLITE_DBCONFIG_DQS_DML</td> | |
| 2401 | +** <dt>SQLITE_DBCONFIG_DQS_DML</dt> | |
| 2402 | 2402 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2403 | 2403 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2404 | 2404 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2405 | 2405 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2406 | 2406 | ** compile-time option. |
| 2407 | 2407 | ** </dd> |
| 2408 | 2408 | ** |
| 2409 | 2409 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2410 | -** <dt>SQLITE_DBCONFIG_DQS_DDL</td> | |
| 2410 | +** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> | |
| 2411 | 2411 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2412 | 2412 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2413 | 2413 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2414 | 2414 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2415 | 2415 | ** compile-time option. |
| 2416 | 2416 | ** </dd> |
| 2417 | 2417 | ** |
| 2418 | 2418 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2419 | -** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td> | |
| 2419 | +** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> | |
| 2420 | 2420 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2421 | 2421 | ** assume that database schemas are untainted by malicious content. |
| 2422 | 2422 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2423 | 2423 | ** takes additional defensive steps to protect the application from harm |
| 2424 | 2424 | ** including: |
| @@ -2434,20 +2434,20 @@ | ||
| 2434 | 2434 | ** all applications are advised to turn it off if possible. This setting |
| 2435 | 2435 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2436 | 2436 | ** </dd> |
| 2437 | 2437 | ** |
| 2438 | 2438 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2439 | -** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td> | |
| 2439 | +** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> | |
| 2440 | 2440 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2441 | 2441 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2442 | 2442 | ** created database file to have a schema format version number (the 4-byte |
| 2443 | 2443 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2444 | 2444 | ** means that the resulting database file will be readable and writable by |
| 2445 | 2445 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2446 | 2446 | ** newly created databases are generally not understandable by SQLite versions |
| 2447 | 2447 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2448 | -** is now scarcely any need to generated database files that are compatible | |
| 2448 | +** is now scarcely any need to generate database files that are compatible | |
| 2449 | 2449 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2450 | 2450 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2451 | 2451 | ** ability to generate new database files that are compatible with version |
| 2452 | 2452 | ** 3.0.0. |
| 2453 | 2453 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2456,27 +2456,39 @@ | ||
| 2456 | 2456 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2457 | 2457 | ** either generated columns or decending indexes. |
| 2458 | 2458 | ** </dd> |
| 2459 | 2459 | ** |
| 2460 | 2460 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2461 | -** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td> | |
| 2461 | +** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> | |
| 2462 | 2462 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2463 | 2463 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2464 | 2464 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2465 | 2465 | ** statistics. For statistics to be collected, the flag must be set on |
| 2466 | 2466 | ** the database handle both when the SQL statement is prepared and when it |
| 2467 | 2467 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2468 | -** by default.</dd> | |
| 2468 | +** by default. This option takes two arguments: an integer and a pointer to | |
| 2469 | +** an integer.. The first argument is 1, 0, or -1 to enable, disable, or | |
| 2470 | +** leave unchanged the statement scanstatus option. If the second argument | |
| 2471 | +** is not NULL, then the value of the statement scanstatus setting after | |
| 2472 | +** processing the first argument is written into the integer that the second | |
| 2473 | +** argument points to. | |
| 2474 | +** </dd> | |
| 2469 | 2475 | ** |
| 2470 | 2476 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2471 | -** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</td> | |
| 2472 | -** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option change the default order | |
| 2477 | +** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> | |
| 2478 | +** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order | |
| 2473 | 2479 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2474 | 2480 | ** and work toward the beginning rather than starting at the beginning and |
| 2475 | -** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the | |
| 2476 | -** same as setting [PRAGMA reverse_unordered_selects]. This configuration option | |
| 2477 | -** is useful for application testing.</dd> | |
| 2481 | +** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the | |
| 2482 | +** same as setting [PRAGMA reverse_unordered_selects]. This option takes | |
| 2483 | +** two arguments which are an integer and a pointer to an integer. The first | |
| 2484 | +** argument is 1, 0, or -1 to enable, disable, or leave unchanged the | |
| 2485 | +** reverse scan order flag, respectively. If the second argument is not NULL, | |
| 2486 | +** then 0 or 1 is written into the integer that the second argument points to | |
| 2487 | +** depending on if the reverse scan order flag is set after processing the | |
| 2488 | +** first argument. | |
| 2489 | +** </dd> | |
| 2478 | 2490 | ** |
| 2479 | 2491 | ** </dl> |
| 2480 | 2492 | */ |
| 2481 | 2493 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2482 | 2494 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2494,11 +2506,11 @@ | ||
| 2494 | 2506 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2495 | 2507 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2496 | 2508 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2497 | 2509 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2498 | 2510 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2499 | -#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ | |
| 2511 | +#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ | |
| 2500 | 2512 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2501 | 2513 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2502 | 2514 | |
| 2503 | 2515 | /* |
| 2504 | 2516 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6241,10 +6253,17 @@ | ||
| 6241 | 6253 | ** ^SQLite implements this interface by calling the xSleep() |
| 6242 | 6254 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6243 | 6255 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6244 | 6256 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6245 | 6257 | ** in the previous paragraphs. |
| 6258 | +** | |
| 6259 | +** If a negative argument is passed to sqlite3_sleep() the results vary by | |
| 6260 | +** VFS and operating system. Some system treat a negative argument as an | |
| 6261 | +** instruction to sleep forever. Others understand it to mean do not sleep | |
| 6262 | +** at all. ^In SQLite version 3.42.0 and later, a negative | |
| 6263 | +** argument passed into sqlite3_sleep() is changed to zero before it is relayed | |
| 6264 | +** down into the xSleep method of the VFS. | |
| 6246 | 6265 | */ |
| 6247 | 6266 | SQLITE_API int sqlite3_sleep(int); |
| 6248 | 6267 | |
| 6249 | 6268 | /* |
| 6250 | 6269 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -7868,13 +7887,13 @@ | ||
| 7868 | 7887 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 7869 | 7888 | ** previously entered by the same thread. The behavior |
| 7870 | 7889 | ** is undefined if the mutex is not currently entered by the |
| 7871 | 7890 | ** calling thread or is not currently allocated. |
| 7872 | 7891 | ** |
| 7873 | -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or | |
| 7874 | -** sqlite3_mutex_leave() is a NULL pointer, then all three routines | |
| 7875 | -** behave as no-ops. | |
| 7892 | +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), | |
| 7893 | +** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, | |
| 7894 | +** then any of the four routines behaves as a no-op. | |
| 7876 | 7895 | ** |
| 7877 | 7896 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 7878 | 7897 | */ |
| 7879 | 7898 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 7880 | 7899 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -10800,20 +10819,24 @@ | ||
| 10800 | 10819 | ** [sqlite3session_create()] for details. |
| 10801 | 10820 | */ |
| 10802 | 10821 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 10803 | 10822 | |
| 10804 | 10823 | /* |
| 10805 | -** CAPIREF: Conigure a Session Object | |
| 10824 | +** CAPI3REF: Configure a Session Object | |
| 10806 | 10825 | ** METHOD: sqlite3_session |
| 10807 | 10826 | ** |
| 10808 | 10827 | ** This method is used to configure a session object after it has been |
| 10809 | -** created. At present the only valid value for the second parameter is | |
| 10810 | -** [SQLITE_SESSION_OBJCONFIG_SIZE]. | |
| 10828 | +** created. At present the only valid values for the second parameter are | |
| 10829 | +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. | |
| 10811 | 10830 | ** |
| 10812 | -** Arguments for sqlite3session_object_config() | |
| 10831 | +*/ | |
| 10832 | +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); | |
| 10833 | + | |
| 10834 | +/* | |
| 10835 | +** CAPI3REF: Options for sqlite3session_object_config | |
| 10813 | 10836 | ** |
| 10814 | -** The following values may passed as the the 4th parameter to | |
| 10837 | +** The following values may passed as the the 2nd parameter to | |
| 10815 | 10838 | ** sqlite3session_object_config(). |
| 10816 | 10839 | ** |
| 10817 | 10840 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 10818 | 10841 | ** This option is used to set, clear or query the flag that enables |
| 10819 | 10842 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -10825,16 +10848,25 @@ | ||
| 10825 | 10848 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 10826 | 10849 | ** enabled following the current call, or 0 otherwise. |
| 10827 | 10850 | ** |
| 10828 | 10851 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 10829 | 10852 | ** the first table has been attached to the session object. |
| 10830 | -*/ | |
| 10831 | -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); | |
| 10832 | - | |
| 10833 | -/* | |
| 10853 | +** | |
| 10854 | +** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> | |
| 10855 | +** This option is used to set, clear or query the flag that enables | |
| 10856 | +** collection of data for tables with no explicit PRIMARY KEY. | |
| 10857 | +** | |
| 10858 | +** Normally, tables with no explicit PRIMARY KEY are simply ignored | |
| 10859 | +** by the sessions module. However, if this flag is set, it behaves | |
| 10860 | +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted | |
| 10861 | +** as their leftmost columns. | |
| 10862 | +** | |
| 10863 | +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after | |
| 10864 | +** the first table has been attached to the session object. | |
| 10834 | 10865 | */ |
| 10835 | -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 | |
| 10866 | +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 | |
| 10867 | +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 | |
| 10836 | 10868 | |
| 10837 | 10869 | /* |
| 10838 | 10870 | ** CAPI3REF: Enable Or Disable A Session Object |
| 10839 | 10871 | ** METHOD: sqlite3_session |
| 10840 | 10872 | ** |
| 10841 | 10873 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.42.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3042000 |
| 151 | #define SQLITE_SOURCE_ID "2023-04-10 18:44:00 4c5a3c5fb351cc1c2ce16c33314ce19c53531f09263f87456283d9d756002f9d" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -2140,32 +2140,32 @@ | |
| 2140 | ** configuration setting is never used, then the default maximum is determined |
| 2141 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2142 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2143 | ** </dl> |
| 2144 | */ |
| 2145 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 2146 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 2147 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| 2148 | #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ |
| 2149 | #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| 2150 | #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ |
| 2151 | #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ |
| 2152 | #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ |
| 2153 | #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ |
| 2154 | #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ |
| 2155 | #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ |
| 2156 | /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ |
| 2157 | #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ |
| 2158 | #define SQLITE_CONFIG_PCACHE 14 /* no-op */ |
| 2159 | #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ |
| 2160 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ |
| 2161 | #define SQLITE_CONFIG_URI 17 /* int */ |
| 2162 | #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ |
| 2163 | #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ |
| 2164 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2165 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 2166 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 2167 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2168 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2169 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2170 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2171 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2396,29 +2396,29 @@ | |
| 2396 | ** additional information. This feature can also be turned on and off |
| 2397 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2398 | ** </dd> |
| 2399 | ** |
| 2400 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2401 | ** <dt>SQLITE_DBCONFIG_DQS_DML</td> |
| 2402 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2403 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2404 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2405 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2406 | ** compile-time option. |
| 2407 | ** </dd> |
| 2408 | ** |
| 2409 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2410 | ** <dt>SQLITE_DBCONFIG_DQS_DDL</td> |
| 2411 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2412 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2413 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2414 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2415 | ** compile-time option. |
| 2416 | ** </dd> |
| 2417 | ** |
| 2418 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2419 | ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td> |
| 2420 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2421 | ** assume that database schemas are untainted by malicious content. |
| 2422 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2423 | ** takes additional defensive steps to protect the application from harm |
| 2424 | ** including: |
| @@ -2434,20 +2434,20 @@ | |
| 2434 | ** all applications are advised to turn it off if possible. This setting |
| 2435 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2436 | ** </dd> |
| 2437 | ** |
| 2438 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2439 | ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td> |
| 2440 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2441 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2442 | ** created database file to have a schema format version number (the 4-byte |
| 2443 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2444 | ** means that the resulting database file will be readable and writable by |
| 2445 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2446 | ** newly created databases are generally not understandable by SQLite versions |
| 2447 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2448 | ** is now scarcely any need to generated database files that are compatible |
| 2449 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2450 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2451 | ** ability to generate new database files that are compatible with version |
| 2452 | ** 3.0.0. |
| 2453 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2456,27 +2456,39 @@ | |
| 2456 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2457 | ** either generated columns or decending indexes. |
| 2458 | ** </dd> |
| 2459 | ** |
| 2460 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2461 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td> |
| 2462 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2463 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2464 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2465 | ** statistics. For statistics to be collected, the flag must be set on |
| 2466 | ** the database handle both when the SQL statement is prepared and when it |
| 2467 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2468 | ** by default.</dd> |
| 2469 | ** |
| 2470 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2471 | ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</td> |
| 2472 | ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option change the default order |
| 2473 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2474 | ** and work toward the beginning rather than starting at the beginning and |
| 2475 | ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the |
| 2476 | ** same as setting [PRAGMA reverse_unordered_selects]. This configuration option |
| 2477 | ** is useful for application testing.</dd> |
| 2478 | ** |
| 2479 | ** </dl> |
| 2480 | */ |
| 2481 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2482 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2494,11 +2506,11 @@ | |
| 2494 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2495 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2496 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2497 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2498 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2499 | #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ |
| 2500 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2501 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2502 | |
| 2503 | /* |
| 2504 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6241,10 +6253,17 @@ | |
| 6241 | ** ^SQLite implements this interface by calling the xSleep() |
| 6242 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6243 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6244 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6245 | ** in the previous paragraphs. |
| 6246 | */ |
| 6247 | SQLITE_API int sqlite3_sleep(int); |
| 6248 | |
| 6249 | /* |
| 6250 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -7868,13 +7887,13 @@ | |
| 7868 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 7869 | ** previously entered by the same thread. The behavior |
| 7870 | ** is undefined if the mutex is not currently entered by the |
| 7871 | ** calling thread or is not currently allocated. |
| 7872 | ** |
| 7873 | ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or |
| 7874 | ** sqlite3_mutex_leave() is a NULL pointer, then all three routines |
| 7875 | ** behave as no-ops. |
| 7876 | ** |
| 7877 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 7878 | */ |
| 7879 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 7880 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -10800,20 +10819,24 @@ | |
| 10800 | ** [sqlite3session_create()] for details. |
| 10801 | */ |
| 10802 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 10803 | |
| 10804 | /* |
| 10805 | ** CAPIREF: Conigure a Session Object |
| 10806 | ** METHOD: sqlite3_session |
| 10807 | ** |
| 10808 | ** This method is used to configure a session object after it has been |
| 10809 | ** created. At present the only valid value for the second parameter is |
| 10810 | ** [SQLITE_SESSION_OBJCONFIG_SIZE]. |
| 10811 | ** |
| 10812 | ** Arguments for sqlite3session_object_config() |
| 10813 | ** |
| 10814 | ** The following values may passed as the the 4th parameter to |
| 10815 | ** sqlite3session_object_config(). |
| 10816 | ** |
| 10817 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 10818 | ** This option is used to set, clear or query the flag that enables |
| 10819 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -10825,16 +10848,25 @@ | |
| 10825 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 10826 | ** enabled following the current call, or 0 otherwise. |
| 10827 | ** |
| 10828 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 10829 | ** the first table has been attached to the session object. |
| 10830 | */ |
| 10831 | SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); |
| 10832 | |
| 10833 | /* |
| 10834 | */ |
| 10835 | #define SQLITE_SESSION_OBJCONFIG_SIZE 1 |
| 10836 | |
| 10837 | /* |
| 10838 | ** CAPI3REF: Enable Or Disable A Session Object |
| 10839 | ** METHOD: sqlite3_session |
| 10840 | ** |
| 10841 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.42.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3042000 |
| 151 | #define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -2140,32 +2140,32 @@ | |
| 2140 | ** configuration setting is never used, then the default maximum is determined |
| 2141 | ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that |
| 2142 | ** compile-time option is not set, then the default maximum is 1073741824. |
| 2143 | ** </dl> |
| 2144 | */ |
| 2145 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 2146 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 2147 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| 2148 | #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ |
| 2149 | #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| 2150 | #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ |
| 2151 | #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ |
| 2152 | #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ |
| 2153 | #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ |
| 2154 | #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ |
| 2155 | #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ |
| 2156 | /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ |
| 2157 | #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ |
| 2158 | #define SQLITE_CONFIG_PCACHE 14 /* no-op */ |
| 2159 | #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ |
| 2160 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ |
| 2161 | #define SQLITE_CONFIG_URI 17 /* int */ |
| 2162 | #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ |
| 2163 | #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ |
| 2164 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 2165 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 2166 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 2167 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 2168 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 2169 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 2170 | #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ |
| 2171 | #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ |
| @@ -2396,29 +2396,29 @@ | |
| 2396 | ** additional information. This feature can also be turned on and off |
| 2397 | ** using the [PRAGMA legacy_alter_table] statement. |
| 2398 | ** </dd> |
| 2399 | ** |
| 2400 | ** [[SQLITE_DBCONFIG_DQS_DML]] |
| 2401 | ** <dt>SQLITE_DBCONFIG_DQS_DML</dt> |
| 2402 | ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates |
| 2403 | ** the legacy [double-quoted string literal] misfeature for DML statements |
| 2404 | ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The |
| 2405 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2406 | ** compile-time option. |
| 2407 | ** </dd> |
| 2408 | ** |
| 2409 | ** [[SQLITE_DBCONFIG_DQS_DDL]] |
| 2410 | ** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> |
| 2411 | ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates |
| 2412 | ** the legacy [double-quoted string literal] misfeature for DDL statements, |
| 2413 | ** such as CREATE TABLE and CREATE INDEX. The |
| 2414 | ** default value of this setting is determined by the [-DSQLITE_DQS] |
| 2415 | ** compile-time option. |
| 2416 | ** </dd> |
| 2417 | ** |
| 2418 | ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] |
| 2419 | ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> |
| 2420 | ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to |
| 2421 | ** assume that database schemas are untainted by malicious content. |
| 2422 | ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite |
| 2423 | ** takes additional defensive steps to protect the application from harm |
| 2424 | ** including: |
| @@ -2434,20 +2434,20 @@ | |
| 2434 | ** all applications are advised to turn it off if possible. This setting |
| 2435 | ** can also be controlled using the [PRAGMA trusted_schema] statement. |
| 2436 | ** </dd> |
| 2437 | ** |
| 2438 | ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] |
| 2439 | ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> |
| 2440 | ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates |
| 2441 | ** the legacy file format flag. When activated, this flag causes all newly |
| 2442 | ** created database file to have a schema format version number (the 4-byte |
| 2443 | ** integer found at offset 44 into the database header) of 1. This in turn |
| 2444 | ** means that the resulting database file will be readable and writable by |
| 2445 | ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, |
| 2446 | ** newly created databases are generally not understandable by SQLite versions |
| 2447 | ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there |
| 2448 | ** is now scarcely any need to generate database files that are compatible |
| 2449 | ** all the way back to version 3.0.0, and so this setting is of little |
| 2450 | ** practical use, but is provided so that SQLite can continue to claim the |
| 2451 | ** ability to generate new database files that are compatible with version |
| 2452 | ** 3.0.0. |
| 2453 | ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, |
| @@ -2456,27 +2456,39 @@ | |
| 2456 | ** not considered a bug since SQLite versions 3.3.0 and earlier do not support |
| 2457 | ** either generated columns or decending indexes. |
| 2458 | ** </dd> |
| 2459 | ** |
| 2460 | ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] |
| 2461 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> |
| 2462 | ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in |
| 2463 | ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears |
| 2464 | ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() |
| 2465 | ** statistics. For statistics to be collected, the flag must be set on |
| 2466 | ** the database handle both when the SQL statement is prepared and when it |
| 2467 | ** is stepped. The flag is set (collection of statistics is enabled) |
| 2468 | ** by default. This option takes two arguments: an integer and a pointer to |
| 2469 | ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or |
| 2470 | ** leave unchanged the statement scanstatus option. If the second argument |
| 2471 | ** is not NULL, then the value of the statement scanstatus setting after |
| 2472 | ** processing the first argument is written into the integer that the second |
| 2473 | ** argument points to. |
| 2474 | ** </dd> |
| 2475 | ** |
| 2476 | ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] |
| 2477 | ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> |
| 2478 | ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order |
| 2479 | ** in which tables and indexes are scanned so that the scans start at the end |
| 2480 | ** and work toward the beginning rather than starting at the beginning and |
| 2481 | ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the |
| 2482 | ** same as setting [PRAGMA reverse_unordered_selects]. This option takes |
| 2483 | ** two arguments which are an integer and a pointer to an integer. The first |
| 2484 | ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the |
| 2485 | ** reverse scan order flag, respectively. If the second argument is not NULL, |
| 2486 | ** then 0 or 1 is written into the integer that the second argument points to |
| 2487 | ** depending on if the reverse scan order flag is set after processing the |
| 2488 | ** first argument. |
| 2489 | ** </dd> |
| 2490 | ** |
| 2491 | ** </dl> |
| 2492 | */ |
| 2493 | #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ |
| 2494 | #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ |
| @@ -2494,11 +2506,11 @@ | |
| 2506 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ |
| 2507 | #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ |
| 2508 | #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ |
| 2509 | #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ |
| 2510 | #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ |
| 2511 | #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ |
| 2512 | #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ |
| 2513 | #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ |
| 2514 | |
| 2515 | /* |
| 2516 | ** CAPI3REF: Enable Or Disable Extended Result Codes |
| @@ -6241,10 +6253,17 @@ | |
| 6253 | ** ^SQLite implements this interface by calling the xSleep() |
| 6254 | ** method of the default [sqlite3_vfs] object. If the xSleep() method |
| 6255 | ** of the default VFS is not implemented correctly, or not implemented at |
| 6256 | ** all, then the behavior of sqlite3_sleep() may deviate from the description |
| 6257 | ** in the previous paragraphs. |
| 6258 | ** |
| 6259 | ** If a negative argument is passed to sqlite3_sleep() the results vary by |
| 6260 | ** VFS and operating system. Some system treat a negative argument as an |
| 6261 | ** instruction to sleep forever. Others understand it to mean do not sleep |
| 6262 | ** at all. ^In SQLite version 3.42.0 and later, a negative |
| 6263 | ** argument passed into sqlite3_sleep() is changed to zero before it is relayed |
| 6264 | ** down into the xSleep method of the VFS. |
| 6265 | */ |
| 6266 | SQLITE_API int sqlite3_sleep(int); |
| 6267 | |
| 6268 | /* |
| 6269 | ** CAPI3REF: Name Of The Folder Holding Temporary Files |
| @@ -7868,13 +7887,13 @@ | |
| 7887 | ** ^The sqlite3_mutex_leave() routine exits a mutex that was |
| 7888 | ** previously entered by the same thread. The behavior |
| 7889 | ** is undefined if the mutex is not currently entered by the |
| 7890 | ** calling thread or is not currently allocated. |
| 7891 | ** |
| 7892 | ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), |
| 7893 | ** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, |
| 7894 | ** then any of the four routines behaves as a no-op. |
| 7895 | ** |
| 7896 | ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. |
| 7897 | */ |
| 7898 | SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); |
| 7899 | SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); |
| @@ -10800,20 +10819,24 @@ | |
| 10819 | ** [sqlite3session_create()] for details. |
| 10820 | */ |
| 10821 | SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); |
| 10822 | |
| 10823 | /* |
| 10824 | ** CAPI3REF: Configure a Session Object |
| 10825 | ** METHOD: sqlite3_session |
| 10826 | ** |
| 10827 | ** This method is used to configure a session object after it has been |
| 10828 | ** created. At present the only valid values for the second parameter are |
| 10829 | ** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. |
| 10830 | ** |
| 10831 | */ |
| 10832 | SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); |
| 10833 | |
| 10834 | /* |
| 10835 | ** CAPI3REF: Options for sqlite3session_object_config |
| 10836 | ** |
| 10837 | ** The following values may passed as the the 2nd parameter to |
| 10838 | ** sqlite3session_object_config(). |
| 10839 | ** |
| 10840 | ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> |
| 10841 | ** This option is used to set, clear or query the flag that enables |
| 10842 | ** the [sqlite3session_changeset_size()] API. Because it imposes some |
| @@ -10825,16 +10848,25 @@ | |
| 10848 | ** variable is set to 1 if the sqlite3session_changeset_size() API is |
| 10849 | ** enabled following the current call, or 0 otherwise. |
| 10850 | ** |
| 10851 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 10852 | ** the first table has been attached to the session object. |
| 10853 | ** |
| 10854 | ** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> |
| 10855 | ** This option is used to set, clear or query the flag that enables |
| 10856 | ** collection of data for tables with no explicit PRIMARY KEY. |
| 10857 | ** |
| 10858 | ** Normally, tables with no explicit PRIMARY KEY are simply ignored |
| 10859 | ** by the sessions module. However, if this flag is set, it behaves |
| 10860 | ** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted |
| 10861 | ** as their leftmost columns. |
| 10862 | ** |
| 10863 | ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after |
| 10864 | ** the first table has been attached to the session object. |
| 10865 | */ |
| 10866 | #define SQLITE_SESSION_OBJCONFIG_SIZE 1 |
| 10867 | #define SQLITE_SESSION_OBJCONFIG_ROWID 2 |
| 10868 | |
| 10869 | /* |
| 10870 | ** CAPI3REF: Enable Or Disable A Session Object |
| 10871 | ** METHOD: sqlite3_session |
| 10872 | ** |
| 10873 |
+6
-1
| --- skins/ardoise/css.txt | ||
| +++ skins/ardoise/css.txt | ||
| @@ -1387,11 +1387,16 @@ | ||
| 1387 | 1387 | } |
| 1388 | 1388 | .intLink[title=Hyperlink]:hover { |
| 1389 | 1389 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=) |
| 1390 | 1390 | } |
| 1391 | 1391 | .statistics-report-graph-line { |
| 1392 | - background-color: #ff8000 | |
| 1392 | + border: 2px solid #ff8000; | |
| 1393 | + background-color: #ff8000; | |
| 1394 | +} | |
| 1395 | +.statistics-report-graph-extra { | |
| 1396 | + border: 2px dashed #446979; | |
| 1397 | + border-left-style: none; | |
| 1393 | 1398 | } |
| 1394 | 1399 | mark, |
| 1395 | 1400 | p.noMoreShun, |
| 1396 | 1401 | p.shunned, |
| 1397 | 1402 | span.modpending { |
| 1398 | 1403 |
| --- skins/ardoise/css.txt | |
| +++ skins/ardoise/css.txt | |
| @@ -1387,11 +1387,16 @@ | |
| 1387 | } |
| 1388 | .intLink[title=Hyperlink]:hover { |
| 1389 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=) |
| 1390 | } |
| 1391 | .statistics-report-graph-line { |
| 1392 | background-color: #ff8000 |
| 1393 | } |
| 1394 | mark, |
| 1395 | p.noMoreShun, |
| 1396 | p.shunned, |
| 1397 | span.modpending { |
| 1398 |
| --- skins/ardoise/css.txt | |
| +++ skins/ardoise/css.txt | |
| @@ -1387,11 +1387,16 @@ | |
| 1387 | } |
| 1388 | .intLink[title=Hyperlink]:hover { |
| 1389 | background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=) |
| 1390 | } |
| 1391 | .statistics-report-graph-line { |
| 1392 | border: 2px solid #ff8000; |
| 1393 | background-color: #ff8000; |
| 1394 | } |
| 1395 | .statistics-report-graph-extra { |
| 1396 | border: 2px dashed #446979; |
| 1397 | border-left-style: none; |
| 1398 | } |
| 1399 | mark, |
| 1400 | p.noMoreShun, |
| 1401 | p.shunned, |
| 1402 | span.modpending { |
| 1403 |
+5
| --- skins/eagle/css.txt | ||
| +++ skins/eagle/css.txt | ||
| @@ -416,11 +416,16 @@ | ||
| 416 | 416 | table.numbered-lines td.line-numbers span.selected-line { |
| 417 | 417 | background-color: #7EA2D9; |
| 418 | 418 | } |
| 419 | 419 | |
| 420 | 420 | .statistics-report-graph-line { |
| 421 | + border: 2px solid #7EA2D9; | |
| 421 | 422 | background-color: #7EA2D9; |
| 423 | +} | |
| 424 | +.statistics-report-graph-extra { | |
| 425 | + border: 2px solid #7EA2D9; | |
| 426 | + border-left-style: none; | |
| 422 | 427 | } |
| 423 | 428 | |
| 424 | 429 | .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { |
| 425 | 430 | background-color: #455978; |
| 426 | 431 | } |
| 427 | 432 |
| --- skins/eagle/css.txt | |
| +++ skins/eagle/css.txt | |
| @@ -416,11 +416,16 @@ | |
| 416 | table.numbered-lines td.line-numbers span.selected-line { |
| 417 | background-color: #7EA2D9; |
| 418 | } |
| 419 | |
| 420 | .statistics-report-graph-line { |
| 421 | background-color: #7EA2D9; |
| 422 | } |
| 423 | |
| 424 | .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { |
| 425 | background-color: #455978; |
| 426 | } |
| 427 |
| --- skins/eagle/css.txt | |
| +++ skins/eagle/css.txt | |
| @@ -416,11 +416,16 @@ | |
| 416 | table.numbered-lines td.line-numbers span.selected-line { |
| 417 | background-color: #7EA2D9; |
| 418 | } |
| 419 | |
| 420 | .statistics-report-graph-line { |
| 421 | border: 2px solid #7EA2D9; |
| 422 | background-color: #7EA2D9; |
| 423 | } |
| 424 | .statistics-report-graph-extra { |
| 425 | border: 2px solid #7EA2D9; |
| 426 | border-left-style: none; |
| 427 | } |
| 428 | |
| 429 | .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] { |
| 430 | background-color: #455978; |
| 431 | } |
| 432 |
+5
| --- skins/xekri/css.txt | ||
| +++ skins/xekri/css.txt | ||
| @@ -513,11 +513,16 @@ | ||
| 513 | 513 | /************************************** |
| 514 | 514 | * Statistics Reports |
| 515 | 515 | */ |
| 516 | 516 | |
| 517 | 517 | .statistics-report-graph-line { |
| 518 | + border: 2px solid #22e; | |
| 518 | 519 | background-color: #22e; |
| 520 | +} | |
| 521 | +.statistics-report-graph-extra { | |
| 522 | + border: 2px dashed #22e; | |
| 523 | + border-left-style: none; | |
| 519 | 524 | } |
| 520 | 525 | |
| 521 | 526 | .statistics-report-table-events th { |
| 522 | 527 | padding: 0 1rem; |
| 523 | 528 | } |
| 524 | 529 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -513,11 +513,16 @@ | |
| 513 | /************************************** |
| 514 | * Statistics Reports |
| 515 | */ |
| 516 | |
| 517 | .statistics-report-graph-line { |
| 518 | background-color: #22e; |
| 519 | } |
| 520 | |
| 521 | .statistics-report-table-events th { |
| 522 | padding: 0 1rem; |
| 523 | } |
| 524 |
| --- skins/xekri/css.txt | |
| +++ skins/xekri/css.txt | |
| @@ -513,11 +513,16 @@ | |
| 513 | /************************************** |
| 514 | * Statistics Reports |
| 515 | */ |
| 516 | |
| 517 | .statistics-report-graph-line { |
| 518 | border: 2px solid #22e; |
| 519 | background-color: #22e; |
| 520 | } |
| 521 | .statistics-report-graph-extra { |
| 522 | border: 2px dashed #22e; |
| 523 | border-left-style: none; |
| 524 | } |
| 525 | |
| 526 | .statistics-report-table-events th { |
| 527 | padding: 0 1rem; |
| 528 | } |
| 529 |
+19
-3
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -186,11 +186,11 @@ | ||
| 186 | 186 | " ON CONFLICT(eventId) DO NOTHING;\n" |
| 187 | 187 | "END;" |
| 188 | 188 | ); |
| 189 | 189 | } |
| 190 | 190 | if( db_table_exists("repository","chat") |
| 191 | - && db_get("chat-timeline-user", "")[0]!=0 | |
| 191 | + && db_get("chat-timeline-user", "")[0]!=0 | |
| 192 | 192 | ){ |
| 193 | 193 | /* Record events that will be relayed to chat, but do not relay |
| 194 | 194 | ** them immediately, as the chat_msg_from_event() function requires |
| 195 | 195 | ** that TAGXREF be up-to-date, and that has not happened yet when |
| 196 | 196 | ** the insert into the EVENT table occurs. Make arrangements to |
| @@ -233,10 +233,26 @@ | ||
| 233 | 233 | int alert_enabled(void){ |
| 234 | 234 | if( !alert_tables_exist() ) return 0; |
| 235 | 235 | if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0; |
| 236 | 236 | return 1; |
| 237 | 237 | } |
| 238 | + | |
| 239 | +/* | |
| 240 | +** If alerts are enabled, removes the pending_alert entry which | |
| 241 | +** matches (eventType || rid). Note that pending_alert entries are | |
| 242 | +** added via the manifest crosslinking process, so this has no effect | |
| 243 | +** if called before crosslinking is performed. Because alerts are sent | |
| 244 | +** asynchronously, unqueuing needs to be performed as part of the | |
| 245 | +** transaction in which crosslinking is performed in order to avoid a | |
| 246 | +** race condition. | |
| 247 | +*/ | |
| 248 | +void alert_unqueue(char eventType, int rid){ | |
| 249 | + if( alert_enabled() ){ | |
| 250 | + db_multi_exec("DELETE FROM pending_alert WHERE eventid='%c%d'", | |
| 251 | + eventType, rid); | |
| 252 | + } | |
| 253 | +} | |
| 238 | 254 | |
| 239 | 255 | /* |
| 240 | 256 | ** If the subscriber table does not exist, then paint an error message |
| 241 | 257 | ** web page and return true. |
| 242 | 258 | ** |
| @@ -301,11 +317,11 @@ | ||
| 301 | 317 | } |
| 302 | 318 | @ </table> |
| 303 | 319 | @ <hr> |
| 304 | 320 | @ <h1> Configuration </h1> |
| 305 | 321 | @ <form action="%R/setup_notification" method="post"><div> |
| 306 | - @ <input type="submit" name="submit" value="Apply Changes" /><hr> | |
| 322 | + @ <input type="submit" name="submit" value="Apply Changes"><hr> | |
| 307 | 323 | login_insert_csrf_secret(); |
| 308 | 324 | |
| 309 | 325 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 310 | 326 | "eurl", "", 0); |
| 311 | 327 | @ <p><b>Required.</b> |
| @@ -400,11 +416,11 @@ | ||
| 400 | 416 | @ append a colon and TCP port number (ex: smtp.example.com:587). |
| 401 | 417 | @ The default TCP port number is 25. |
| 402 | 418 | @ (Property: "email-send-relayhost")</p> |
| 403 | 419 | @ <hr> |
| 404 | 420 | |
| 405 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 421 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 406 | 422 | @ </div></form> |
| 407 | 423 | db_end_transaction(0); |
| 408 | 424 | style_finish_page(); |
| 409 | 425 | } |
| 410 | 426 | |
| 411 | 427 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -186,11 +186,11 @@ | |
| 186 | " ON CONFLICT(eventId) DO NOTHING;\n" |
| 187 | "END;" |
| 188 | ); |
| 189 | } |
| 190 | if( db_table_exists("repository","chat") |
| 191 | && db_get("chat-timeline-user", "")[0]!=0 |
| 192 | ){ |
| 193 | /* Record events that will be relayed to chat, but do not relay |
| 194 | ** them immediately, as the chat_msg_from_event() function requires |
| 195 | ** that TAGXREF be up-to-date, and that has not happened yet when |
| 196 | ** the insert into the EVENT table occurs. Make arrangements to |
| @@ -233,10 +233,26 @@ | |
| 233 | int alert_enabled(void){ |
| 234 | if( !alert_tables_exist() ) return 0; |
| 235 | if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0; |
| 236 | return 1; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | ** If the subscriber table does not exist, then paint an error message |
| 241 | ** web page and return true. |
| 242 | ** |
| @@ -301,11 +317,11 @@ | |
| 301 | } |
| 302 | @ </table> |
| 303 | @ <hr> |
| 304 | @ <h1> Configuration </h1> |
| 305 | @ <form action="%R/setup_notification" method="post"><div> |
| 306 | @ <input type="submit" name="submit" value="Apply Changes" /><hr> |
| 307 | login_insert_csrf_secret(); |
| 308 | |
| 309 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 310 | "eurl", "", 0); |
| 311 | @ <p><b>Required.</b> |
| @@ -400,11 +416,11 @@ | |
| 400 | @ append a colon and TCP port number (ex: smtp.example.com:587). |
| 401 | @ The default TCP port number is 25. |
| 402 | @ (Property: "email-send-relayhost")</p> |
| 403 | @ <hr> |
| 404 | |
| 405 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 406 | @ </div></form> |
| 407 | db_end_transaction(0); |
| 408 | style_finish_page(); |
| 409 | } |
| 410 | |
| 411 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -186,11 +186,11 @@ | |
| 186 | " ON CONFLICT(eventId) DO NOTHING;\n" |
| 187 | "END;" |
| 188 | ); |
| 189 | } |
| 190 | if( db_table_exists("repository","chat") |
| 191 | && db_get("chat-timeline-user", "")[0]!=0 |
| 192 | ){ |
| 193 | /* Record events that will be relayed to chat, but do not relay |
| 194 | ** them immediately, as the chat_msg_from_event() function requires |
| 195 | ** that TAGXREF be up-to-date, and that has not happened yet when |
| 196 | ** the insert into the EVENT table occurs. Make arrangements to |
| @@ -233,10 +233,26 @@ | |
| 233 | int alert_enabled(void){ |
| 234 | if( !alert_tables_exist() ) return 0; |
| 235 | if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0; |
| 236 | return 1; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | ** If alerts are enabled, removes the pending_alert entry which |
| 241 | ** matches (eventType || rid). Note that pending_alert entries are |
| 242 | ** added via the manifest crosslinking process, so this has no effect |
| 243 | ** if called before crosslinking is performed. Because alerts are sent |
| 244 | ** asynchronously, unqueuing needs to be performed as part of the |
| 245 | ** transaction in which crosslinking is performed in order to avoid a |
| 246 | ** race condition. |
| 247 | */ |
| 248 | void alert_unqueue(char eventType, int rid){ |
| 249 | if( alert_enabled() ){ |
| 250 | db_multi_exec("DELETE FROM pending_alert WHERE eventid='%c%d'", |
| 251 | eventType, rid); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | ** If the subscriber table does not exist, then paint an error message |
| 257 | ** web page and return true. |
| 258 | ** |
| @@ -301,11 +317,11 @@ | |
| 317 | } |
| 318 | @ </table> |
| 319 | @ <hr> |
| 320 | @ <h1> Configuration </h1> |
| 321 | @ <form action="%R/setup_notification" method="post"><div> |
| 322 | @ <input type="submit" name="submit" value="Apply Changes"><hr> |
| 323 | login_insert_csrf_secret(); |
| 324 | |
| 325 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 326 | "eurl", "", 0); |
| 327 | @ <p><b>Required.</b> |
| @@ -400,11 +416,11 @@ | |
| 416 | @ append a colon and TCP port number (ex: smtp.example.com:587). |
| 417 | @ The default TCP port number is 25. |
| 418 | @ (Property: "email-send-relayhost")</p> |
| 419 | @ <hr> |
| 420 | |
| 421 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 422 | @ </div></form> |
| 423 | db_end_transaction(0); |
| 424 | style_finish_page(); |
| 425 | } |
| 426 | |
| 427 |
+14
-14
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -111,15 +111,15 @@ | ||
| 111 | 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | 112 | } |
| 113 | 113 | @ <li><p> |
| 114 | 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | 115 | moderation_pending_www(attachid); |
| 116 | - @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 116 | + @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 117 | 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | 119 | if( zComment && zComment[0] ){ |
| 120 | - @ %!W(zComment)<br /> | |
| 120 | + @ %!W(zComment)<br> | |
| 121 | 121 | } |
| 122 | 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | 124 | zSrc = "Deleted from"; |
| 125 | 125 | }else { |
| @@ -397,23 +397,23 @@ | ||
| 397 | 397 | } |
| 398 | 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | 400 | @ <div> |
| 401 | 401 | @ File to Attach: |
| 402 | - @ <input type="file" name="f" size="60" /><br /> | |
| 403 | - @ Description:<br /> | |
| 404 | - @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br /> | |
| 402 | + @ <input type="file" name="f" size="60"><br> | |
| 403 | + @ Description:<br> | |
| 404 | + @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br> | |
| 405 | 405 | if( zTkt ){ |
| 406 | - @ <input type="hidden" name="tkt" value="%h(zTkt)" /> | |
| 406 | + @ <input type="hidden" name="tkt" value="%h(zTkt)"> | |
| 407 | 407 | }else if( zTechNote ){ |
| 408 | - @ <input type="hidden" name="technote" value="%h(zTechNote)" /> | |
| 408 | + @ <input type="hidden" name="technote" value="%h(zTechNote)"> | |
| 409 | 409 | }else{ |
| 410 | - @ <input type="hidden" name="page" value="%h(zPage)" /> | |
| 410 | + @ <input type="hidden" name="page" value="%h(zPage)"> | |
| 411 | 411 | } |
| 412 | - @ <input type="hidden" name="from" value="%h(zFrom)" /> | |
| 413 | - @ <input type="submit" name="ok" value="Add Attachment" /> | |
| 414 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 412 | + @ <input type="hidden" name="from" value="%h(zFrom)"> | |
| 413 | + @ <input type="submit" name="ok" value="Add Attachment"> | |
| 414 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 415 | 415 | @ </div> |
| 416 | 416 | captcha_generate(0); |
| 417 | 417 | @ </form> |
| 418 | 418 | style_finish_page(); |
| 419 | 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | ||
| 591 | 591 | if( isModerator && modPending ){ |
| 592 | 592 | @ <div class="section">Moderation</div> |
| 593 | 593 | @ <blockquote> |
| 594 | 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | - @ Delete this change</label><br /> | |
| 596 | + @ Delete this change</label><br> | |
| 597 | 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | - @ Approve this change</label><br /> | |
| 598 | + @ Approve this change</label><br> | |
| 599 | 599 | @ <input type="submit" value="Submit"> |
| 600 | 600 | @ </form> |
| 601 | 601 | @ </blockquote> |
| 602 | 602 | } |
| 603 | 603 | |
| @@ -616,11 +616,11 @@ | ||
| 616 | 616 | @ %h(z) |
| 617 | 617 | @ </pre> |
| 618 | 618 | } |
| 619 | 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | - @ <i>(file is %d(sz) bytes of image data)</i><br /> | |
| 621 | + @ <i>(file is %d(sz) bytes of image data)</i><br> | |
| 622 | 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | 624 | }else{ |
| 625 | 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 | 627 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -111,15 +111,15 @@ | |
| 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | } |
| 113 | @ <li><p> |
| 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | moderation_pending_www(attachid); |
| 116 | @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | if( zComment && zComment[0] ){ |
| 120 | @ %!W(zComment)<br /> |
| 121 | } |
| 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | zSrc = "Deleted from"; |
| 125 | }else { |
| @@ -397,23 +397,23 @@ | |
| 397 | } |
| 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | @ <div> |
| 401 | @ File to Attach: |
| 402 | @ <input type="file" name="f" size="60" /><br /> |
| 403 | @ Description:<br /> |
| 404 | @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br /> |
| 405 | if( zTkt ){ |
| 406 | @ <input type="hidden" name="tkt" value="%h(zTkt)" /> |
| 407 | }else if( zTechNote ){ |
| 408 | @ <input type="hidden" name="technote" value="%h(zTechNote)" /> |
| 409 | }else{ |
| 410 | @ <input type="hidden" name="page" value="%h(zPage)" /> |
| 411 | } |
| 412 | @ <input type="hidden" name="from" value="%h(zFrom)" /> |
| 413 | @ <input type="submit" name="ok" value="Add Attachment" /> |
| 414 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 415 | @ </div> |
| 416 | captcha_generate(0); |
| 417 | @ </form> |
| 418 | style_finish_page(); |
| 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | |
| 591 | if( isModerator && modPending ){ |
| 592 | @ <div class="section">Moderation</div> |
| 593 | @ <blockquote> |
| 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | @ Delete this change</label><br /> |
| 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | @ Approve this change</label><br /> |
| 599 | @ <input type="submit" value="Submit"> |
| 600 | @ </form> |
| 601 | @ </blockquote> |
| 602 | } |
| 603 | |
| @@ -616,11 +616,11 @@ | |
| 616 | @ %h(z) |
| 617 | @ </pre> |
| 618 | } |
| 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | @ <i>(file is %d(sz) bytes of image data)</i><br /> |
| 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | }else{ |
| 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -111,15 +111,15 @@ | |
| 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | } |
| 113 | @ <li><p> |
| 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | moderation_pending_www(attachid); |
| 116 | @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | if( zComment && zComment[0] ){ |
| 120 | @ %!W(zComment)<br> |
| 121 | } |
| 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | zSrc = "Deleted from"; |
| 125 | }else { |
| @@ -397,23 +397,23 @@ | |
| 397 | } |
| 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | @ <div> |
| 401 | @ File to Attach: |
| 402 | @ <input type="file" name="f" size="60"><br> |
| 403 | @ Description:<br> |
| 404 | @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br> |
| 405 | if( zTkt ){ |
| 406 | @ <input type="hidden" name="tkt" value="%h(zTkt)"> |
| 407 | }else if( zTechNote ){ |
| 408 | @ <input type="hidden" name="technote" value="%h(zTechNote)"> |
| 409 | }else{ |
| 410 | @ <input type="hidden" name="page" value="%h(zPage)"> |
| 411 | } |
| 412 | @ <input type="hidden" name="from" value="%h(zFrom)"> |
| 413 | @ <input type="submit" name="ok" value="Add Attachment"> |
| 414 | @ <input type="submit" name="cancel" value="Cancel"> |
| 415 | @ </div> |
| 416 | captcha_generate(0); |
| 417 | @ </form> |
| 418 | style_finish_page(); |
| 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | |
| 591 | if( isModerator && modPending ){ |
| 592 | @ <div class="section">Moderation</div> |
| 593 | @ <blockquote> |
| 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | @ Delete this change</label><br> |
| 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | @ Approve this change</label><br> |
| 599 | @ <input type="submit" value="Submit"> |
| 600 | @ </form> |
| 601 | @ </blockquote> |
| 602 | } |
| 603 | |
| @@ -616,11 +616,11 @@ | |
| 616 | @ %h(z) |
| 617 | @ </pre> |
| 618 | } |
| 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | @ <i>(file is %d(sz) bytes of image data)</i><br> |
| 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | }else{ |
| 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 |
+14
-14
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -111,15 +111,15 @@ | ||
| 111 | 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | 112 | } |
| 113 | 113 | @ <li><p> |
| 114 | 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | 115 | moderation_pending_www(attachid); |
| 116 | - @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 116 | + @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 117 | 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | 119 | if( zComment && zComment[0] ){ |
| 120 | - @ %!W(zComment)<br /> | |
| 120 | + @ %!W(zComment)<br> | |
| 121 | 121 | } |
| 122 | 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | 124 | zSrc = "Deleted from"; |
| 125 | 125 | }else { |
| @@ -397,23 +397,23 @@ | ||
| 397 | 397 | } |
| 398 | 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | 400 | @ <div> |
| 401 | 401 | @ File to Attach: |
| 402 | - @ <input type="file" name="f" size="60" /><br /> | |
| 403 | - @ Description:<br /> | |
| 404 | - @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br /> | |
| 402 | + @ <input type="file" name="f" size="60"><br> | |
| 403 | + @ Description:<br> | |
| 404 | + @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br> | |
| 405 | 405 | if( zTkt ){ |
| 406 | - @ <input type="hidden" name="tkt" value="%h(zTkt)" /> | |
| 406 | + @ <input type="hidden" name="tkt" value="%h(zTkt)"> | |
| 407 | 407 | }else if( zTechNote ){ |
| 408 | - @ <input type="hidden" name="technote" value="%h(zTechNote)" /> | |
| 408 | + @ <input type="hidden" name="technote" value="%h(zTechNote)"> | |
| 409 | 409 | }else{ |
| 410 | - @ <input type="hidden" name="page" value="%h(zPage)" /> | |
| 410 | + @ <input type="hidden" name="page" value="%h(zPage)"> | |
| 411 | 411 | } |
| 412 | - @ <input type="hidden" name="from" value="%h(zFrom)" /> | |
| 413 | - @ <input type="submit" name="ok" value="Add Attachment" /> | |
| 414 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 412 | + @ <input type="hidden" name="from" value="%h(zFrom)"> | |
| 413 | + @ <input type="submit" name="ok" value="Add Attachment"> | |
| 414 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 415 | 415 | @ </div> |
| 416 | 416 | captcha_generate(0); |
| 417 | 417 | @ </form> |
| 418 | 418 | style_finish_page(); |
| 419 | 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | ||
| 591 | 591 | if( isModerator && modPending ){ |
| 592 | 592 | @ <div class="section">Moderation</div> |
| 593 | 593 | @ <blockquote> |
| 594 | 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | - @ Delete this change</label><br /> | |
| 596 | + @ Delete this change</label><br> | |
| 597 | 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | - @ Approve this change</label><br /> | |
| 598 | + @ Approve this change</label><br> | |
| 599 | 599 | @ <input type="submit" value="Submit"> |
| 600 | 600 | @ </form> |
| 601 | 601 | @ </blockquote> |
| 602 | 602 | } |
| 603 | 603 | |
| @@ -616,11 +616,11 @@ | ||
| 616 | 616 | @ %h(z) |
| 617 | 617 | @ </pre> |
| 618 | 618 | } |
| 619 | 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | - @ <i>(file is %d(sz) bytes of image data)</i><br /> | |
| 621 | + @ <i>(file is %d(sz) bytes of image data)</i><br> | |
| 622 | 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | 624 | }else{ |
| 625 | 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 | 627 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -111,15 +111,15 @@ | |
| 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | } |
| 113 | @ <li><p> |
| 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | moderation_pending_www(attachid); |
| 116 | @ <br /><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | if( zComment && zComment[0] ){ |
| 120 | @ %!W(zComment)<br /> |
| 121 | } |
| 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | zSrc = "Deleted from"; |
| 125 | }else { |
| @@ -397,23 +397,23 @@ | |
| 397 | } |
| 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | @ <div> |
| 401 | @ File to Attach: |
| 402 | @ <input type="file" name="f" size="60" /><br /> |
| 403 | @ Description:<br /> |
| 404 | @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br /> |
| 405 | if( zTkt ){ |
| 406 | @ <input type="hidden" name="tkt" value="%h(zTkt)" /> |
| 407 | }else if( zTechNote ){ |
| 408 | @ <input type="hidden" name="technote" value="%h(zTechNote)" /> |
| 409 | }else{ |
| 410 | @ <input type="hidden" name="page" value="%h(zPage)" /> |
| 411 | } |
| 412 | @ <input type="hidden" name="from" value="%h(zFrom)" /> |
| 413 | @ <input type="submit" name="ok" value="Add Attachment" /> |
| 414 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 415 | @ </div> |
| 416 | captcha_generate(0); |
| 417 | @ </form> |
| 418 | style_finish_page(); |
| 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | |
| 591 | if( isModerator && modPending ){ |
| 592 | @ <div class="section">Moderation</div> |
| 593 | @ <blockquote> |
| 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | @ Delete this change</label><br /> |
| 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | @ Approve this change</label><br /> |
| 599 | @ <input type="submit" value="Submit"> |
| 600 | @ </form> |
| 601 | @ </blockquote> |
| 602 | } |
| 603 | |
| @@ -616,11 +616,11 @@ | |
| 616 | @ %h(z) |
| 617 | @ </pre> |
| 618 | } |
| 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | @ <i>(file is %d(sz) bytes of image data)</i><br /> |
| 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | }else{ |
| 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -111,15 +111,15 @@ | |
| 111 | zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); |
| 112 | } |
| 113 | @ <li><p> |
| 114 | @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> |
| 115 | moderation_pending_www(attachid); |
| 116 | @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 117 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br> |
| 118 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 119 | if( zComment && zComment[0] ){ |
| 120 | @ %!W(zComment)<br> |
| 121 | } |
| 122 | if( zPage==0 && zTkt==0 && zTechNote==0 ){ |
| 123 | if( zSrc==0 || zSrc[0]==0 ){ |
| 124 | zSrc = "Deleted from"; |
| 125 | }else { |
| @@ -397,23 +397,23 @@ | |
| 397 | } |
| 398 | @ <h2>Add Attachment To %s(zTargetType)</h2> |
| 399 | form_begin("enctype='multipart/form-data'", "%R/attachadd"); |
| 400 | @ <div> |
| 401 | @ File to Attach: |
| 402 | @ <input type="file" name="f" size="60"><br> |
| 403 | @ Description:<br> |
| 404 | @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br> |
| 405 | if( zTkt ){ |
| 406 | @ <input type="hidden" name="tkt" value="%h(zTkt)"> |
| 407 | }else if( zTechNote ){ |
| 408 | @ <input type="hidden" name="technote" value="%h(zTechNote)"> |
| 409 | }else{ |
| 410 | @ <input type="hidden" name="page" value="%h(zPage)"> |
| 411 | } |
| 412 | @ <input type="hidden" name="from" value="%h(zFrom)"> |
| 413 | @ <input type="submit" name="ok" value="Add Attachment"> |
| 414 | @ <input type="submit" name="cancel" value="Cancel"> |
| 415 | @ </div> |
| 416 | captcha_generate(0); |
| 417 | @ </form> |
| 418 | style_finish_page(); |
| 419 | fossil_free(zTargetType); |
| @@ -591,13 +591,13 @@ | |
| 591 | if( isModerator && modPending ){ |
| 592 | @ <div class="section">Moderation</div> |
| 593 | @ <blockquote> |
| 594 | form_begin(0, "%R/ainfo/%s", zUuid); |
| 595 | @ <label><input type="radio" name="modaction" value="delete"> |
| 596 | @ Delete this change</label><br> |
| 597 | @ <label><input type="radio" name="modaction" value="approve"> |
| 598 | @ Approve this change</label><br> |
| 599 | @ <input type="submit" value="Submit"> |
| 600 | @ </form> |
| 601 | @ </blockquote> |
| 602 | } |
| 603 | |
| @@ -616,11 +616,11 @@ | |
| 616 | @ %h(z) |
| 617 | @ </pre> |
| 618 | } |
| 619 | }else if( strncmp(zMime, "image/", 6)==0 ){ |
| 620 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 621 | @ <i>(file is %d(sz) bytes of image data)</i><br> |
| 622 | @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> |
| 623 | style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime); |
| 624 | }else{ |
| 625 | int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); |
| 626 | @ <i>(file is %d(sz) bytes of binary data)</i> |
| 627 |
+87
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -851,10 +851,97 @@ | ||
| 851 | 851 | if( pTo ){ |
| 852 | 852 | blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); |
| 853 | 853 | } |
| 854 | 854 | pFrom->iCursor = i; |
| 855 | 855 | } |
| 856 | + | |
| 857 | +/* | |
| 858 | +** Remove comment lines (starting with '#') from a blob pIn. | |
| 859 | +** Keep lines starting with "\#" but remove the initial backslash. | |
| 860 | +** | |
| 861 | +** Store the result in pOut. It is ok for pIn and pOut to be the same blob. | |
| 862 | +** | |
| 863 | +** pOut must either be the same as pIn or else uninitialized. | |
| 864 | +*/ | |
| 865 | +void blob_strip_comment_lines(Blob *pIn, Blob *pOut){ | |
| 866 | + char *z = pIn->aData; | |
| 867 | + unsigned int i = 0; | |
| 868 | + unsigned int n = pIn->nUsed; | |
| 869 | + unsigned int lineStart = 0; | |
| 870 | + unsigned int copyStart = 0; | |
| 871 | + int doCopy = 1; | |
| 872 | + Blob temp; | |
| 873 | + blob_zero(&temp); | |
| 874 | + | |
| 875 | + while( i<n ){ | |
| 876 | + if( i==lineStart && z[i]=='#' ){ | |
| 877 | + copyStart = i; | |
| 878 | + doCopy = 0; | |
| 879 | + }else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){ | |
| 880 | + /* keep lines starting with an escaped '#' (and unescape it) */ | |
| 881 | + copyStart = i + 1; | |
| 882 | + } | |
| 883 | + if( z[i]=='\n' ){ | |
| 884 | + if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1); | |
| 885 | + lineStart = copyStart = i + 1; | |
| 886 | + doCopy = 1; | |
| 887 | + } | |
| 888 | + i++; | |
| 889 | + } | |
| 890 | + /* Last line */ | |
| 891 | + if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart); | |
| 892 | + | |
| 893 | + if( pOut==pIn ) blob_reset(pOut); | |
| 894 | + *pOut = temp; | |
| 895 | +} | |
| 896 | + | |
| 897 | +/* | |
| 898 | +** COMMAND: test-strip-comment-lines | |
| 899 | +** | |
| 900 | +** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE | |
| 901 | +** | |
| 902 | +** Read INPUTFILE and print it without comment lines (starting with '#'). | |
| 903 | +** Keep lines starting with "\\#" but remove the initial backslash. | |
| 904 | +** | |
| 905 | +** This is used to test and debug the blob_strip_comment_lines() routine. | |
| 906 | +** | |
| 907 | +** Options: | |
| 908 | +** -y|--side-by-side Show diff of INPUTFILE and output side-by-side | |
| 909 | +** -W|--width N Width of lines in side-by-side diff | |
| 910 | +*/ | |
| 911 | +void test_strip_comment_lines_cmd(void){ | |
| 912 | + Blob f, h; /* unitialized */ | |
| 913 | + Blob out; | |
| 914 | + DiffConfig dCfg; | |
| 915 | + int sbs = 0; | |
| 916 | + const char *z; | |
| 917 | + int w = 0; | |
| 918 | + | |
| 919 | + memset(&dCfg, 0, sizeof(dCfg)); | |
| 920 | + | |
| 921 | + sbs = find_option("side-by-side","y",0)!=0; | |
| 922 | + if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ | |
| 923 | + dCfg.wColumn = w; | |
| 924 | + } | |
| 925 | + verify_all_options(); | |
| 926 | + if( g.argc!=3 ) usage("INPUTFILE"); | |
| 927 | + | |
| 928 | + blob_read_from_file(&f, g.argv[2], ExtFILE); | |
| 929 | + blob_strip_comment_lines(&f, &h); | |
| 930 | + | |
| 931 | + if ( !sbs ){ | |
| 932 | + blob_write_to_file(&h, "-"); | |
| 933 | + }else{ | |
| 934 | + blob_zero(&out); | |
| 935 | + dCfg.nContext = -1; /* whole content */ | |
| 936 | + dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR; | |
| 937 | + diff_begin(&dCfg); | |
| 938 | + text_diff(&f, &h, &out, &dCfg); | |
| 939 | + blob_write_to_file(&out, "-"); | |
| 940 | + diff_end(&dCfg, 0); | |
| 941 | + } | |
| 942 | +} | |
| 856 | 943 | |
| 857 | 944 | /* |
| 858 | 945 | ** Ensure that the text in pBlob ends with '\n' |
| 859 | 946 | */ |
| 860 | 947 | void blob_add_final_newline(Blob *pBlob){ |
| 861 | 948 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -851,10 +851,97 @@ | |
| 851 | if( pTo ){ |
| 852 | blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); |
| 853 | } |
| 854 | pFrom->iCursor = i; |
| 855 | } |
| 856 | |
| 857 | /* |
| 858 | ** Ensure that the text in pBlob ends with '\n' |
| 859 | */ |
| 860 | void blob_add_final_newline(Blob *pBlob){ |
| 861 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -851,10 +851,97 @@ | |
| 851 | if( pTo ){ |
| 852 | blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); |
| 853 | } |
| 854 | pFrom->iCursor = i; |
| 855 | } |
| 856 | |
| 857 | /* |
| 858 | ** Remove comment lines (starting with '#') from a blob pIn. |
| 859 | ** Keep lines starting with "\#" but remove the initial backslash. |
| 860 | ** |
| 861 | ** Store the result in pOut. It is ok for pIn and pOut to be the same blob. |
| 862 | ** |
| 863 | ** pOut must either be the same as pIn or else uninitialized. |
| 864 | */ |
| 865 | void blob_strip_comment_lines(Blob *pIn, Blob *pOut){ |
| 866 | char *z = pIn->aData; |
| 867 | unsigned int i = 0; |
| 868 | unsigned int n = pIn->nUsed; |
| 869 | unsigned int lineStart = 0; |
| 870 | unsigned int copyStart = 0; |
| 871 | int doCopy = 1; |
| 872 | Blob temp; |
| 873 | blob_zero(&temp); |
| 874 | |
| 875 | while( i<n ){ |
| 876 | if( i==lineStart && z[i]=='#' ){ |
| 877 | copyStart = i; |
| 878 | doCopy = 0; |
| 879 | }else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){ |
| 880 | /* keep lines starting with an escaped '#' (and unescape it) */ |
| 881 | copyStart = i + 1; |
| 882 | } |
| 883 | if( z[i]=='\n' ){ |
| 884 | if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1); |
| 885 | lineStart = copyStart = i + 1; |
| 886 | doCopy = 1; |
| 887 | } |
| 888 | i++; |
| 889 | } |
| 890 | /* Last line */ |
| 891 | if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart); |
| 892 | |
| 893 | if( pOut==pIn ) blob_reset(pOut); |
| 894 | *pOut = temp; |
| 895 | } |
| 896 | |
| 897 | /* |
| 898 | ** COMMAND: test-strip-comment-lines |
| 899 | ** |
| 900 | ** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE |
| 901 | ** |
| 902 | ** Read INPUTFILE and print it without comment lines (starting with '#'). |
| 903 | ** Keep lines starting with "\\#" but remove the initial backslash. |
| 904 | ** |
| 905 | ** This is used to test and debug the blob_strip_comment_lines() routine. |
| 906 | ** |
| 907 | ** Options: |
| 908 | ** -y|--side-by-side Show diff of INPUTFILE and output side-by-side |
| 909 | ** -W|--width N Width of lines in side-by-side diff |
| 910 | */ |
| 911 | void test_strip_comment_lines_cmd(void){ |
| 912 | Blob f, h; /* unitialized */ |
| 913 | Blob out; |
| 914 | DiffConfig dCfg; |
| 915 | int sbs = 0; |
| 916 | const char *z; |
| 917 | int w = 0; |
| 918 | |
| 919 | memset(&dCfg, 0, sizeof(dCfg)); |
| 920 | |
| 921 | sbs = find_option("side-by-side","y",0)!=0; |
| 922 | if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ |
| 923 | dCfg.wColumn = w; |
| 924 | } |
| 925 | verify_all_options(); |
| 926 | if( g.argc!=3 ) usage("INPUTFILE"); |
| 927 | |
| 928 | blob_read_from_file(&f, g.argv[2], ExtFILE); |
| 929 | blob_strip_comment_lines(&f, &h); |
| 930 | |
| 931 | if ( !sbs ){ |
| 932 | blob_write_to_file(&h, "-"); |
| 933 | }else{ |
| 934 | blob_zero(&out); |
| 935 | dCfg.nContext = -1; /* whole content */ |
| 936 | dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR; |
| 937 | diff_begin(&dCfg); |
| 938 | text_diff(&f, &h, &out, &dCfg); |
| 939 | blob_write_to_file(&out, "-"); |
| 940 | diff_end(&dCfg, 0); |
| 941 | } |
| 942 | } |
| 943 | |
| 944 | /* |
| 945 | ** Ensure that the text in pBlob ends with '\n' |
| 946 | */ |
| 947 | void blob_add_final_newline(Blob *pBlob){ |
| 948 |
+2
-2
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -1164,13 +1164,13 @@ | ||
| 1164 | 1164 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1165 | 1165 | const char *zFile = db_column_text(&q2,0); |
| 1166 | 1166 | @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ |
| 1167 | 1167 | if( showId ){ |
| 1168 | 1168 | int fid = db_column_int(&q2,1); |
| 1169 | - @ (%d(fid))<br /> | |
| 1169 | + @ (%d(fid))<br> | |
| 1170 | 1170 | }else{ |
| 1171 | - @ </a><br /> | |
| 1171 | + @ </a><br> | |
| 1172 | 1172 | } |
| 1173 | 1173 | } |
| 1174 | 1174 | db_reset(&q2); |
| 1175 | 1175 | @ </td> |
| 1176 | 1176 | @ <td> |
| 1177 | 1177 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -1164,13 +1164,13 @@ | |
| 1164 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1165 | const char *zFile = db_column_text(&q2,0); |
| 1166 | @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ |
| 1167 | if( showId ){ |
| 1168 | int fid = db_column_int(&q2,1); |
| 1169 | @ (%d(fid))<br /> |
| 1170 | }else{ |
| 1171 | @ </a><br /> |
| 1172 | } |
| 1173 | } |
| 1174 | db_reset(&q2); |
| 1175 | @ </td> |
| 1176 | @ <td> |
| 1177 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -1164,13 +1164,13 @@ | |
| 1164 | while( db_step(&q2)==SQLITE_ROW ){ |
| 1165 | const char *zFile = db_column_text(&q2,0); |
| 1166 | @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \ |
| 1167 | if( showId ){ |
| 1168 | int fid = db_column_int(&q2,1); |
| 1169 | @ (%d(fid))<br> |
| 1170 | }else{ |
| 1171 | @ </a><br> |
| 1172 | } |
| 1173 | } |
| 1174 | db_reset(&q2); |
| 1175 | @ </td> |
| 1176 | @ <td> |
| 1177 |
+1
-1
| --- src/cache.c | ||
| +++ src/cache.c | ||
| @@ -423,11 +423,11 @@ | ||
| 423 | 423 | if( pStmt ){ |
| 424 | 424 | @ <ol> |
| 425 | 425 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 426 | 426 | const unsigned char *zName = sqlite3_column_text(pStmt,0); |
| 427 | 427 | char *zHash = cache_hash_of_key((const char*)zName); |
| 428 | - @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br /> | |
| 428 | + @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> | |
| 429 | 429 | @ size: %,lld(sqlite3_column_int64(pStmt,1)) |
| 430 | 430 | @ hit-count: %d(sqlite3_column_int(pStmt,2)) |
| 431 | 431 | @ last-access: %s(sqlite3_column_text(pStmt,3)) \ |
| 432 | 432 | if( zHash ){ |
| 433 | 433 | @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\ |
| 434 | 434 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -423,11 +423,11 @@ | |
| 423 | if( pStmt ){ |
| 424 | @ <ol> |
| 425 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 426 | const unsigned char *zName = sqlite3_column_text(pStmt,0); |
| 427 | char *zHash = cache_hash_of_key((const char*)zName); |
| 428 | @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br /> |
| 429 | @ size: %,lld(sqlite3_column_int64(pStmt,1)) |
| 430 | @ hit-count: %d(sqlite3_column_int(pStmt,2)) |
| 431 | @ last-access: %s(sqlite3_column_text(pStmt,3)) \ |
| 432 | if( zHash ){ |
| 433 | @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\ |
| 434 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -423,11 +423,11 @@ | |
| 423 | if( pStmt ){ |
| 424 | @ <ol> |
| 425 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 426 | const unsigned char *zName = sqlite3_column_text(pStmt,0); |
| 427 | char *zHash = cache_hash_of_key((const char*)zName); |
| 428 | @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> |
| 429 | @ size: %,lld(sqlite3_column_int64(pStmt,1)) |
| 430 | @ hit-count: %d(sqlite3_column_int(pStmt,2)) |
| 431 | @ last-access: %s(sqlite3_column_text(pStmt,3)) \ |
| 432 | if( zHash ){ |
| 433 | @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\ |
| 434 |
+3
-3
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -544,12 +544,12 @@ | ||
| 544 | 544 | zCaptcha = captcha_render(zDecoded); |
| 545 | 545 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 546 | 546 | @ %h(zCaptcha) |
| 547 | 547 | @ </pre> |
| 548 | 548 | @ Enter security code shown above: |
| 549 | - @ <input type="hidden" name="captchaseed" value="%u(uSeed)" /> | |
| 550 | - @ <input type="text" name="captcha" size=8 /> | |
| 549 | + @ <input type="hidden" name="captchaseed" value="%u(uSeed)"> | |
| 550 | + @ <input type="text" name="captcha" size=8> | |
| 551 | 551 | if( showButton ){ |
| 552 | 552 | @ <input type="submit" value="Submit"> |
| 553 | 553 | } |
| 554 | 554 | @ <br/>\ |
| 555 | 555 | captcha_speakit_button(uSeed, 0); |
| @@ -685,11 +685,11 @@ | ||
| 685 | 685 | ** |
| 686 | 686 | ** Return a WAV file that pronounces the digits of the captcha that |
| 687 | 687 | ** is determined by the seed given in the name= query parameter. |
| 688 | 688 | */ |
| 689 | 689 | void captcha_wav_page(void){ |
| 690 | - const char *zSeed = P("name"); | |
| 690 | + const char *zSeed = PD("name","0"); | |
| 691 | 691 | const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
| 692 | 692 | Blob audio; |
| 693 | 693 | captcha_wav(zDecode, &audio); |
| 694 | 694 | cgi_set_content_type("audio/wav"); |
| 695 | 695 | cgi_set_content(&audio); |
| 696 | 696 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -544,12 +544,12 @@ | |
| 544 | zCaptcha = captcha_render(zDecoded); |
| 545 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 546 | @ %h(zCaptcha) |
| 547 | @ </pre> |
| 548 | @ Enter security code shown above: |
| 549 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 550 | @ <input type="text" name="captcha" size=8 /> |
| 551 | if( showButton ){ |
| 552 | @ <input type="submit" value="Submit"> |
| 553 | } |
| 554 | @ <br/>\ |
| 555 | captcha_speakit_button(uSeed, 0); |
| @@ -685,11 +685,11 @@ | |
| 685 | ** |
| 686 | ** Return a WAV file that pronounces the digits of the captcha that |
| 687 | ** is determined by the seed given in the name= query parameter. |
| 688 | */ |
| 689 | void captcha_wav_page(void){ |
| 690 | const char *zSeed = P("name"); |
| 691 | const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
| 692 | Blob audio; |
| 693 | captcha_wav(zDecode, &audio); |
| 694 | cgi_set_content_type("audio/wav"); |
| 695 | cgi_set_content(&audio); |
| 696 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -544,12 +544,12 @@ | |
| 544 | zCaptcha = captcha_render(zDecoded); |
| 545 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 546 | @ %h(zCaptcha) |
| 547 | @ </pre> |
| 548 | @ Enter security code shown above: |
| 549 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 550 | @ <input type="text" name="captcha" size=8> |
| 551 | if( showButton ){ |
| 552 | @ <input type="submit" value="Submit"> |
| 553 | } |
| 554 | @ <br/>\ |
| 555 | captcha_speakit_button(uSeed, 0); |
| @@ -685,11 +685,11 @@ | |
| 685 | ** |
| 686 | ** Return a WAV file that pronounces the digits of the captcha that |
| 687 | ** is determined by the seed given in the name= query parameter. |
| 688 | */ |
| 689 | void captcha_wav_page(void){ |
| 690 | const char *zSeed = PD("name","0"); |
| 691 | const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
| 692 | Blob audio; |
| 693 | captcha_wav(zDecode, &audio); |
| 694 | cgi_set_content_type("audio/wav"); |
| 695 | cgi_set_content(&audio); |
| 696 |
+3
-1
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -1747,11 +1747,11 @@ | ||
| 1747 | 1747 | if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue; |
| 1748 | 1748 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; |
| 1749 | 1749 | } |
| 1750 | 1750 | switch( eDest ){ |
| 1751 | 1751 | case 0: { |
| 1752 | - cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue); | |
| 1752 | + cgi_printf("%h = %h <br>\n", zName, aParamQP[i].zValue); | |
| 1753 | 1753 | break; |
| 1754 | 1754 | } |
| 1755 | 1755 | case 1: { |
| 1756 | 1756 | fossil_trace("%s = %s\n", zName, aParamQP[i].zValue); |
| 1757 | 1757 | break; |
| @@ -2052,10 +2052,12 @@ | ||
| 2052 | 2052 | cgi_setenv("HTTP_REFERER", zVal); |
| 2053 | 2053 | }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ |
| 2054 | 2054 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 2055 | 2055 | }else if( fossil_strcmp(zFieldName,"authorization:")==0 ){ |
| 2056 | 2056 | cgi_setenv("HTTP_AUTHORIZATION", zVal); |
| 2057 | + }else if( fossil_strcmp(zFieldName,"accept-language:")==0 ){ | |
| 2058 | + cgi_setenv("HTTP_ACCEPT_LANGUAGE", zVal); | |
| 2057 | 2059 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 2058 | 2060 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 2059 | 2061 | if( zIpAddr!=0 ){ |
| 2060 | 2062 | g.zIpAddr = fossil_strdup(zIpAddr); |
| 2061 | 2063 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 2062 | 2064 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1747,11 +1747,11 @@ | |
| 1747 | if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue; |
| 1748 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; |
| 1749 | } |
| 1750 | switch( eDest ){ |
| 1751 | case 0: { |
| 1752 | cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue); |
| 1753 | break; |
| 1754 | } |
| 1755 | case 1: { |
| 1756 | fossil_trace("%s = %s\n", zName, aParamQP[i].zValue); |
| 1757 | break; |
| @@ -2052,10 +2052,12 @@ | |
| 2052 | cgi_setenv("HTTP_REFERER", zVal); |
| 2053 | }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ |
| 2054 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 2055 | }else if( fossil_strcmp(zFieldName,"authorization:")==0 ){ |
| 2056 | cgi_setenv("HTTP_AUTHORIZATION", zVal); |
| 2057 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 2058 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 2059 | if( zIpAddr!=0 ){ |
| 2060 | g.zIpAddr = fossil_strdup(zIpAddr); |
| 2061 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 2062 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1747,11 +1747,11 @@ | |
| 1747 | if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue; |
| 1748 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; |
| 1749 | } |
| 1750 | switch( eDest ){ |
| 1751 | case 0: { |
| 1752 | cgi_printf("%h = %h <br>\n", zName, aParamQP[i].zValue); |
| 1753 | break; |
| 1754 | } |
| 1755 | case 1: { |
| 1756 | fossil_trace("%s = %s\n", zName, aParamQP[i].zValue); |
| 1757 | break; |
| @@ -2052,10 +2052,12 @@ | |
| 2052 | cgi_setenv("HTTP_REFERER", zVal); |
| 2053 | }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ |
| 2054 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 2055 | }else if( fossil_strcmp(zFieldName,"authorization:")==0 ){ |
| 2056 | cgi_setenv("HTTP_AUTHORIZATION", zVal); |
| 2057 | }else if( fossil_strcmp(zFieldName,"accept-language:")==0 ){ |
| 2058 | cgi_setenv("HTTP_ACCEPT_LANGUAGE", zVal); |
| 2059 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 2060 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 2061 | if( zIpAddr!=0 ){ |
| 2062 | g.zIpAddr = fossil_strdup(zIpAddr); |
| 2063 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 2064 |
+5
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1869,10 +1869,12 @@ | ||
| 1869 | 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | + int fHasNul; /* contains NUL chars? */ | |
| 1875 | + int fHasLong; /* overly long line? */ | |
| 1874 | 1876 | char *zMsg; /* Warning message */ |
| 1875 | 1877 | Blob fname; /* Relative pathname of the file */ |
| 1876 | 1878 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1877 | 1879 | |
| 1878 | 1880 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | ||
| 1888 | 1890 | } |
| 1889 | 1891 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1890 | 1892 | fBinary = (lookFlags & LOOK_BINARY); |
| 1891 | 1893 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1892 | 1894 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1895 | + fHasNul = (lookFlags & LOOK_NUL); | |
| 1896 | + fHasLong = (lookFlags & LOOK_LONG); | |
| 1893 | 1897 | }else{ |
| 1894 | 1898 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1895 | - fHasLoneCrOnly = fHasCrLfOnly = 0; | |
| 1899 | + fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0; | |
| 1896 | 1900 | } |
| 1897 | 1901 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1898 | 1902 | const char *zWarning = 0; |
| 1899 | 1903 | const char *zDisable = 0; |
| 1900 | 1904 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | ||
| 1901 | 1905 | const char *zIn = "in"; |
| 1902 | 1906 | Blob ans; |
| 1903 | 1907 | char cReply; |
| 1904 | 1908 | |
| 1905 | 1909 | if( fBinary ){ |
| 1906 | - int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */ | |
| 1907 | - int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */ | |
| 1908 | 1910 | if( binOk ){ |
| 1909 | 1911 | return 0; /* We don't want binary warnings for this file. */ |
| 1910 | 1912 | } |
| 1911 | 1913 | if( !fHasNul && fHasLong ){ |
| 1912 | 1914 | zWarning = "long lines"; |
| 1913 | 1915 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1869,10 +1869,12 @@ | |
| 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | char *zMsg; /* Warning message */ |
| 1875 | Blob fname; /* Relative pathname of the file */ |
| 1876 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1877 | |
| 1878 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | |
| 1888 | } |
| 1889 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1890 | fBinary = (lookFlags & LOOK_BINARY); |
| 1891 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1892 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1893 | }else{ |
| 1894 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1895 | fHasLoneCrOnly = fHasCrLfOnly = 0; |
| 1896 | } |
| 1897 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1898 | const char *zWarning = 0; |
| 1899 | const char *zDisable = 0; |
| 1900 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | |
| 1901 | const char *zIn = "in"; |
| 1902 | Blob ans; |
| 1903 | char cReply; |
| 1904 | |
| 1905 | if( fBinary ){ |
| 1906 | int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */ |
| 1907 | int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */ |
| 1908 | if( binOk ){ |
| 1909 | return 0; /* We don't want binary warnings for this file. */ |
| 1910 | } |
| 1911 | if( !fHasNul && fHasLong ){ |
| 1912 | zWarning = "long lines"; |
| 1913 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1869,10 +1869,12 @@ | |
| 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | int fHasNul; /* contains NUL chars? */ |
| 1875 | int fHasLong; /* overly long line? */ |
| 1876 | char *zMsg; /* Warning message */ |
| 1877 | Blob fname; /* Relative pathname of the file */ |
| 1878 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1879 | |
| 1880 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | |
| 1890 | } |
| 1891 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1892 | fBinary = (lookFlags & LOOK_BINARY); |
| 1893 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1894 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1895 | fHasNul = (lookFlags & LOOK_NUL); |
| 1896 | fHasLong = (lookFlags & LOOK_LONG); |
| 1897 | }else{ |
| 1898 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1899 | fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0; |
| 1900 | } |
| 1901 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1902 | const char *zWarning = 0; |
| 1903 | const char *zDisable = 0; |
| 1904 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | |
| 1905 | const char *zIn = "in"; |
| 1906 | Blob ans; |
| 1907 | char cReply; |
| 1908 | |
| 1909 | if( fBinary ){ |
| 1910 | if( binOk ){ |
| 1911 | return 0; /* We don't want binary warnings for this file. */ |
| 1912 | } |
| 1913 | if( !fHasNul && fHasLong ){ |
| 1914 | zWarning = "long lines"; |
| 1915 |
+5
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1869,10 +1869,12 @@ | ||
| 1869 | 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | + int fHasNul; /* contains NUL chars? */ | |
| 1875 | + int fHasLong; /* overly long line? */ | |
| 1874 | 1876 | char *zMsg; /* Warning message */ |
| 1875 | 1877 | Blob fname; /* Relative pathname of the file */ |
| 1876 | 1878 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1877 | 1879 | |
| 1878 | 1880 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | ||
| 1888 | 1890 | } |
| 1889 | 1891 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1890 | 1892 | fBinary = (lookFlags & LOOK_BINARY); |
| 1891 | 1893 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1892 | 1894 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1895 | + fHasNul = (lookFlags & LOOK_NUL); | |
| 1896 | + fHasLong = (lookFlags & LOOK_LONG); | |
| 1893 | 1897 | }else{ |
| 1894 | 1898 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1895 | - fHasLoneCrOnly = fHasCrLfOnly = 0; | |
| 1899 | + fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0; | |
| 1896 | 1900 | } |
| 1897 | 1901 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1898 | 1902 | const char *zWarning = 0; |
| 1899 | 1903 | const char *zDisable = 0; |
| 1900 | 1904 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | ||
| 1901 | 1905 | const char *zIn = "in"; |
| 1902 | 1906 | Blob ans; |
| 1903 | 1907 | char cReply; |
| 1904 | 1908 | |
| 1905 | 1909 | if( fBinary ){ |
| 1906 | - int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */ | |
| 1907 | - int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */ | |
| 1908 | 1910 | if( binOk ){ |
| 1909 | 1911 | return 0; /* We don't want binary warnings for this file. */ |
| 1910 | 1912 | } |
| 1911 | 1913 | if( !fHasNul && fHasLong ){ |
| 1912 | 1914 | zWarning = "long lines"; |
| 1913 | 1915 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1869,10 +1869,12 @@ | |
| 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | char *zMsg; /* Warning message */ |
| 1875 | Blob fname; /* Relative pathname of the file */ |
| 1876 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1877 | |
| 1878 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | |
| 1888 | } |
| 1889 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1890 | fBinary = (lookFlags & LOOK_BINARY); |
| 1891 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1892 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1893 | }else{ |
| 1894 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1895 | fHasLoneCrOnly = fHasCrLfOnly = 0; |
| 1896 | } |
| 1897 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1898 | const char *zWarning = 0; |
| 1899 | const char *zDisable = 0; |
| 1900 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | |
| 1901 | const char *zIn = "in"; |
| 1902 | Blob ans; |
| 1903 | char cReply; |
| 1904 | |
| 1905 | if( fBinary ){ |
| 1906 | int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */ |
| 1907 | int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */ |
| 1908 | if( binOk ){ |
| 1909 | return 0; /* We don't want binary warnings for this file. */ |
| 1910 | } |
| 1911 | if( !fHasNul && fHasLong ){ |
| 1912 | zWarning = "long lines"; |
| 1913 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1869,10 +1869,12 @@ | |
| 1869 | int lookFlags; /* output flags from looks_like_utf8/utf16() */ |
| 1870 | int fHasAnyCr; /* the blob contains one or more CR chars */ |
| 1871 | int fHasLoneCrOnly; /* all detected line endings are CR only */ |
| 1872 | int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ |
| 1873 | int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */ |
| 1874 | int fHasNul; /* contains NUL chars? */ |
| 1875 | int fHasLong; /* overly long line? */ |
| 1876 | char *zMsg; /* Warning message */ |
| 1877 | Blob fname; /* Relative pathname of the file */ |
| 1878 | static int allOk = 0; /* Set to true to disable this routine */ |
| 1879 | |
| 1880 | if( allOk ) return 0; |
| @@ -1888,13 +1890,15 @@ | |
| 1890 | } |
| 1891 | fHasAnyCr = (lookFlags & LOOK_CR); |
| 1892 | fBinary = (lookFlags & LOOK_BINARY); |
| 1893 | fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); |
| 1894 | fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); |
| 1895 | fHasNul = (lookFlags & LOOK_NUL); |
| 1896 | fHasLong = (lookFlags & LOOK_LONG); |
| 1897 | }else{ |
| 1898 | fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0; |
| 1899 | fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0; |
| 1900 | } |
| 1901 | if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){ |
| 1902 | const char *zWarning = 0; |
| 1903 | const char *zDisable = 0; |
| 1904 | const char *zConvert = "c=convert/"; |
| @@ -1901,12 +1905,10 @@ | |
| 1905 | const char *zIn = "in"; |
| 1906 | Blob ans; |
| 1907 | char cReply; |
| 1908 | |
| 1909 | if( fBinary ){ |
| 1910 | if( binOk ){ |
| 1911 | return 0; /* We don't want binary warnings for this file. */ |
| 1912 | } |
| 1913 | if( !fHasNul && fHasLong ){ |
| 1914 | zWarning = "long lines"; |
| 1915 |
+2
-2
| --- src/color.c | ||
| +++ src/color.c | ||
| @@ -161,19 +161,19 @@ | ||
| 161 | 161 | @ suam declinavit.</p> |
| 162 | 162 | cnt++; |
| 163 | 163 | } |
| 164 | 164 | } |
| 165 | 165 | if( cnt ){ |
| 166 | - @ <hr /> | |
| 166 | + @ <hr> | |
| 167 | 167 | } |
| 168 | 168 | @ <form method="POST"> |
| 169 | 169 | @ <p>Enter candidate branch names below and see them displayed in their |
| 170 | 170 | @ default background colors above.</p> |
| 171 | 171 | for(i=0; i<10; i++){ |
| 172 | 172 | sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); |
| 173 | 173 | zBr = P(zNm); |
| 174 | - @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br /> | |
| 174 | + @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br> | |
| 175 | 175 | } |
| 176 | 176 | @ <input type="submit" value="Submit"> |
| 177 | 177 | @ <input type="submit" name="rand" value="Random"> |
| 178 | 178 | @ </form> |
| 179 | 179 | style_finish_page(); |
| 180 | 180 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -161,19 +161,19 @@ | |
| 161 | @ suam declinavit.</p> |
| 162 | cnt++; |
| 163 | } |
| 164 | } |
| 165 | if( cnt ){ |
| 166 | @ <hr /> |
| 167 | } |
| 168 | @ <form method="POST"> |
| 169 | @ <p>Enter candidate branch names below and see them displayed in their |
| 170 | @ default background colors above.</p> |
| 171 | for(i=0; i<10; i++){ |
| 172 | sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); |
| 173 | zBr = P(zNm); |
| 174 | @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br /> |
| 175 | } |
| 176 | @ <input type="submit" value="Submit"> |
| 177 | @ <input type="submit" name="rand" value="Random"> |
| 178 | @ </form> |
| 179 | style_finish_page(); |
| 180 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -161,19 +161,19 @@ | |
| 161 | @ suam declinavit.</p> |
| 162 | cnt++; |
| 163 | } |
| 164 | } |
| 165 | if( cnt ){ |
| 166 | @ <hr> |
| 167 | } |
| 168 | @ <form method="POST"> |
| 169 | @ <p>Enter candidate branch names below and see them displayed in their |
| 170 | @ default background colors above.</p> |
| 171 | for(i=0; i<10; i++){ |
| 172 | sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); |
| 173 | zBr = P(zNm); |
| 174 | @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br> |
| 175 | } |
| 176 | @ <input type="submit" value="Submit"> |
| 177 | @ <input type="submit" name="rand" value="Random"> |
| 178 | @ </form> |
| 179 | style_finish_page(); |
| 180 |
M
src/db.c
+405
-65
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -31,25 +31,57 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | #include "config.h" |
| 33 | 33 | #if defined(_WIN32) |
| 34 | 34 | # if USE_SEE |
| 35 | 35 | # include <windows.h> |
| 36 | +# define GETPID (int)GetCurrentProcessId | |
| 36 | 37 | # endif |
| 37 | 38 | #else |
| 38 | 39 | # include <pwd.h> |
| 40 | +# if USE_SEE | |
| 41 | +# define GETPID getpid | |
| 42 | +# endif | |
| 39 | 43 | #endif |
| 40 | 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | 45 | # define SQLITE_HAS_CODEC |
| 46 | +#endif | |
| 47 | +#if USE_SEE && defined(__linux__) | |
| 48 | +# include <sys/uio.h> | |
| 42 | 49 | #endif |
| 43 | 50 | #include <sqlite3.h> |
| 44 | 51 | #include <sys/types.h> |
| 45 | 52 | #include <sys/stat.h> |
| 46 | 53 | #include <unistd.h> |
| 47 | 54 | #include <time.h> |
| 55 | + | |
| 56 | +/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ | |
| 57 | +#if USE_SEE | |
| 58 | +#if defined(_WIN32) | |
| 59 | +typedef DWORD PID_T; | |
| 60 | +#else | |
| 61 | +typedef pid_t PID_T; | |
| 62 | +#endif | |
| 63 | +#endif | |
| 64 | + | |
| 48 | 65 | #include "db.h" |
| 49 | 66 | |
| 50 | 67 | #if INTERFACE |
| 68 | +/* | |
| 69 | +** Type definitions used for handling the saved encryption key for SEE. | |
| 70 | +*/ | |
| 71 | +#if !defined(_WIN32) | |
| 72 | +typedef void *LPVOID; | |
| 73 | +typedef size_t SIZE_T; | |
| 74 | +#endif | |
| 75 | + | |
| 76 | +/* | |
| 77 | +** Operations for db_maybe_handle_saved_encryption_key_for_process, et al. | |
| 78 | +*/ | |
| 79 | +#define SEE_KEY_READ ((int)0) | |
| 80 | +#define SEE_KEY_WRITE ((int)1) | |
| 81 | +#define SEE_KEY_ZERO ((int)2) | |
| 82 | + | |
| 51 | 83 | /* |
| 52 | 84 | ** An single SQL statement is represented as an instance of the following |
| 53 | 85 | ** structure. |
| 54 | 86 | */ |
| 55 | 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | ||
| 1539 | 1571 | static char *zSavedKey = 0; |
| 1540 | 1572 | |
| 1541 | 1573 | /* |
| 1542 | 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | 1575 | */ |
| 1544 | -size_t savedKeySize = 0; | |
| 1576 | +static size_t savedKeySize = 0; | |
| 1577 | + | |
| 1578 | +/* | |
| 1579 | +** This function returns non-zero if there is a saved database encryption | |
| 1580 | +** key available. | |
| 1581 | +*/ | |
| 1582 | +int db_have_saved_encryption_key(){ | |
| 1583 | + return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize); | |
| 1584 | +} | |
| 1585 | + | |
| 1586 | +/* | |
| 1587 | +** This function returns non-zero if the specified database encryption key | |
| 1588 | +** is valid. | |
| 1589 | +*/ | |
| 1590 | +int db_is_valid_saved_encryption_key(const char *p, size_t n){ | |
| 1591 | + if( p==0 ) return 0; | |
| 1592 | + if( n==0 ) return 0; | |
| 1593 | + if( p[0]==0 ) return 0; | |
| 1594 | + return 1; | |
| 1595 | +} | |
| 1545 | 1596 | |
| 1546 | 1597 | /* |
| 1547 | 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | 1599 | ** no database encryption key is saved. |
| 1549 | 1600 | */ |
| @@ -1556,10 +1607,36 @@ | ||
| 1556 | 1607 | ** -OR- zero if no database encryption key is saved. |
| 1557 | 1608 | */ |
| 1558 | 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | 1610 | return savedKeySize; |
| 1560 | 1611 | } |
| 1612 | + | |
| 1613 | +/* | |
| 1614 | +** This function arranges for the saved database encryption key buffer | |
| 1615 | +** to be allocated and then sets up the environment variable to allow | |
| 1616 | +** a child process to initialize it with the actual database encryption | |
| 1617 | +** key. | |
| 1618 | +*/ | |
| 1619 | +void db_setup_for_saved_encryption_key(){ | |
| 1620 | + void *p = NULL; | |
| 1621 | + size_t n = 0; | |
| 1622 | + size_t pageSize = 0; | |
| 1623 | + Blob pidKey; | |
| 1624 | + | |
| 1625 | + assert( !db_have_saved_encryption_key() ); | |
| 1626 | + db_unsave_encryption_key(); | |
| 1627 | + fossil_get_page_size(&pageSize); | |
| 1628 | + assert( pageSize>0 ); | |
| 1629 | + p = fossil_secure_alloc_page(&n); | |
| 1630 | + assert( p!=NULL ); | |
| 1631 | + assert( n==pageSize ); | |
| 1632 | + blob_zero(&pidKey); | |
| 1633 | + blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n); | |
| 1634 | + fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey)); | |
| 1635 | + zSavedKey = p; | |
| 1636 | + savedKeySize = n; | |
| 1637 | +} | |
| 1561 | 1638 | |
| 1562 | 1639 | /* |
| 1563 | 1640 | ** This function arranges for the database encryption key to be securely |
| 1564 | 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | 1642 | */ |
| @@ -1569,10 +1646,11 @@ | ||
| 1569 | 1646 | void *p = NULL; |
| 1570 | 1647 | size_t n = 0; |
| 1571 | 1648 | size_t pageSize = 0; |
| 1572 | 1649 | size_t blobSize = 0; |
| 1573 | 1650 | |
| 1651 | + assert( !db_have_saved_encryption_key() ); | |
| 1574 | 1652 | blobSize = blob_size(pKey); |
| 1575 | 1653 | if( blobSize==0 ) return; |
| 1576 | 1654 | fossil_get_page_size(&pageSize); |
| 1577 | 1655 | assert( pageSize>0 ); |
| 1578 | 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | ||
| 1599 | 1677 | |
| 1600 | 1678 | /* |
| 1601 | 1679 | ** This function sets the saved database encryption key to the specified |
| 1602 | 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | 1681 | */ |
| 1604 | -void db_set_saved_encryption_key( | |
| 1682 | +static void db_set_saved_encryption_key( | |
| 1605 | 1683 | Blob *pKey |
| 1606 | 1684 | ){ |
| 1607 | 1685 | if( zSavedKey!=NULL ){ |
| 1608 | 1686 | size_t blobSize = blob_size(pKey); |
| 1609 | 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | ||
| 1619 | 1697 | }else{ |
| 1620 | 1698 | db_save_encryption_key(pKey); |
| 1621 | 1699 | } |
| 1622 | 1700 | } |
| 1623 | 1701 | |
| 1702 | +/* | |
| 1703 | +** WEBPAGE: setseekey | |
| 1704 | +** | |
| 1705 | +** Sets the sets the saved database encryption key to one that gets passed | |
| 1706 | +** via the "key" query string parameter. If the saved database encryption | |
| 1707 | +** key has already been set, does nothing. This web page does not produce | |
| 1708 | +** any output on success or failure. No permissions are required and none | |
| 1709 | +** are checked (partially due to lack of encrypted database access). | |
| 1710 | +** | |
| 1711 | +** Query parameters: | |
| 1712 | +** | |
| 1713 | +** key The string to set as the saved database encryption | |
| 1714 | +** key. | |
| 1715 | +*/ | |
| 1716 | +void db_set_see_key_page(void){ | |
| 1717 | + Blob key; | |
| 1718 | + const char *zKey; | |
| 1719 | + if( db_have_saved_encryption_key() ){ | |
| 1720 | + fossil_trace("SEE: encryption key was already set\n"); | |
| 1721 | + return; | |
| 1722 | + } | |
| 1723 | + zKey = P("key"); | |
| 1724 | + blob_init(&key, 0, 0); | |
| 1725 | + if( zKey!=0 ){ | |
| 1726 | + PID_T processId; | |
| 1727 | + blob_set(&key, zKey); | |
| 1728 | + db_set_saved_encryption_key(&key); | |
| 1729 | + processId = db_maybe_handle_saved_encryption_key_for_process( | |
| 1730 | + SEE_KEY_WRITE | |
| 1731 | + ); | |
| 1732 | + fossil_trace("SEE: set encryption key for process %lu, length %u\n", | |
| 1733 | + (unsigned long)processId, blob_size(&key)); | |
| 1734 | + }else{ | |
| 1735 | + fossil_trace("SEE: no encryption key specified\n"); | |
| 1736 | + } | |
| 1737 | + blob_reset(&key); | |
| 1738 | +} | |
| 1739 | + | |
| 1740 | +/* | |
| 1741 | +** WEBPAGE: unsetseekey | |
| 1742 | +** | |
| 1743 | +** Sets the saved database encryption key to zeros in the current and parent | |
| 1744 | +** Fossil processes. This web page does not produce any output on success | |
| 1745 | +** or failure. Setup permission is required. | |
| 1746 | +*/ | |
| 1747 | +void db_unset_see_key_page(void){ | |
| 1748 | + PID_T processId; | |
| 1749 | + login_check_credentials(); | |
| 1750 | + if( !g.perm.Setup ){ login_needed(0); return; } | |
| 1751 | + processId = db_maybe_handle_saved_encryption_key_for_process( | |
| 1752 | + SEE_KEY_ZERO | |
| 1753 | + ); | |
| 1754 | + fossil_trace("SEE: unset encryption key for process %lu\n", | |
| 1755 | + (unsigned long)processId); | |
| 1756 | +} | |
| 1757 | + | |
| 1758 | +/* | |
| 1759 | +** This function reads the saved database encryption key from the | |
| 1760 | +** specified Fossil parent process. This is only necessary (or | |
| 1761 | +** functional) on Windows or Linux. | |
| 1762 | +*/ | |
| 1763 | +static void db_read_saved_encryption_key_from_process( | |
| 1764 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1765 | + LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ | |
| 1766 | + SIZE_T nSize /* Size of saved key buffer in the parent process. */ | |
| 1767 | +){ | |
| 1768 | + void *p = NULL; | |
| 1769 | + size_t n = 0; | |
| 1770 | + size_t pageSize = 0; | |
| 1771 | + | |
| 1772 | + fossil_get_page_size(&pageSize); | |
| 1773 | + assert( pageSize>0 ); | |
| 1774 | + if( nSize>pageSize ){ | |
| 1775 | + fossil_panic("key too large: %u versus %u", nSize, pageSize); | |
| 1776 | + } | |
| 1777 | + p = fossil_secure_alloc_page(&n); | |
| 1778 | + assert( p!=NULL ); | |
| 1779 | + assert( n==pageSize ); | |
| 1780 | + assert( n>=nSize ); | |
| 1781 | + { | |
| 1782 | +#if defined(_WIN32) | |
| 1783 | + HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); | |
| 1784 | + if( hProcess!=NULL ){ | |
| 1785 | + SIZE_T nRead = 0; | |
| 1786 | + if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ | |
| 1787 | + CloseHandle(hProcess); | |
| 1788 | + if( nRead==nSize ){ | |
| 1789 | + db_unsave_encryption_key(); | |
| 1790 | + zSavedKey = p; | |
| 1791 | + savedKeySize = n; | |
| 1792 | + }else{ | |
| 1793 | + fossil_secure_free_page(p, n); | |
| 1794 | + fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", | |
| 1795 | + nRead, nSize, pAddress, processId); | |
| 1796 | + } | |
| 1797 | + }else{ | |
| 1798 | + CloseHandle(hProcess); | |
| 1799 | + fossil_secure_free_page(p, n); | |
| 1800 | + fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1801 | + pAddress, processId, GetLastError()); | |
| 1802 | + } | |
| 1803 | + }else{ | |
| 1804 | + fossil_secure_free_page(p, n); | |
| 1805 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1806 | + } | |
| 1807 | +#elif defined(__linux__) | |
| 1808 | + ssize_t nRead; | |
| 1809 | + struct iovec liov = {0}; | |
| 1810 | + struct iovec riov = {0}; | |
| 1811 | + liov.iov_base = p; | |
| 1812 | + liov.iov_len = n; | |
| 1813 | + riov.iov_base = pAddress; | |
| 1814 | + riov.iov_len = nSize; | |
| 1815 | + nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0); | |
| 1816 | + if( nRead==nSize ){ | |
| 1817 | + db_unsave_encryption_key(); | |
| 1818 | + zSavedKey = p; | |
| 1819 | + savedKeySize = n; | |
| 1820 | + }else{ | |
| 1821 | + fossil_secure_free_page(p, n); | |
| 1822 | + fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu", | |
| 1823 | + nRead, nSize, pAddress, (unsigned long)processId); | |
| 1824 | + } | |
| 1825 | +#else | |
| 1826 | + fossil_secure_free_page(p, n); | |
| 1827 | + fossil_trace("db_read_saved_encryption_key_from_process unsupported"); | |
| 1828 | +#endif | |
| 1829 | + } | |
| 1830 | +} | |
| 1831 | + | |
| 1832 | +/* | |
| 1833 | +** This function writes the saved database encryption key into the | |
| 1834 | +** specified Fossil parent process. This is only necessary (or | |
| 1835 | +** functional) on Windows or Linux. | |
| 1836 | +*/ | |
| 1837 | +static void db_write_saved_encryption_key_to_process( | |
| 1838 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1839 | + LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ | |
| 1840 | + SIZE_T nSize /* Size of saved key buffer in the parent process. */ | |
| 1841 | +){ | |
| 1842 | + void *p = db_get_saved_encryption_key(); | |
| 1843 | + size_t n = db_get_saved_encryption_key_size(); | |
| 1844 | + size_t pageSize = 0; | |
| 1845 | + | |
| 1846 | + fossil_get_page_size(&pageSize); | |
| 1847 | + assert( pageSize>0 ); | |
| 1848 | + if( nSize>pageSize ){ | |
| 1849 | + fossil_panic("key too large: %u versus %u", nSize, pageSize); | |
| 1850 | + } | |
| 1851 | + assert( p!=NULL ); | |
| 1852 | + assert( n==pageSize ); | |
| 1853 | + assert( n>=nSize ); | |
| 1854 | + { | |
| 1624 | 1855 | #if defined(_WIN32) |
| 1856 | + HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, | |
| 1857 | + FALSE, processId); | |
| 1858 | + if( hProcess!=NULL ){ | |
| 1859 | + SIZE_T nWrite = 0; | |
| 1860 | + if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ | |
| 1861 | + CloseHandle(hProcess); | |
| 1862 | + if( nWrite!=nSize ){ | |
| 1863 | + fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu", | |
| 1864 | + nWrite, nSize, pAddress, processId); | |
| 1865 | + } | |
| 1866 | + }else{ | |
| 1867 | + CloseHandle(hProcess); | |
| 1868 | + fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1869 | + pAddress, processId, GetLastError()); | |
| 1870 | + } | |
| 1871 | + }else{ | |
| 1872 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1873 | + } | |
| 1874 | +#elif defined(__linux__) | |
| 1875 | + ssize_t nWrite; | |
| 1876 | + struct iovec liov = {0}; | |
| 1877 | + struct iovec riov = {0}; | |
| 1878 | + liov.iov_base = p; | |
| 1879 | + liov.iov_len = n; | |
| 1880 | + riov.iov_base = pAddress; | |
| 1881 | + riov.iov_len = nSize; | |
| 1882 | + nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); | |
| 1883 | + if( nWrite!=nSize ){ | |
| 1884 | + fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu", | |
| 1885 | + nWrite, nSize, pAddress, (unsigned long)processId); | |
| 1886 | + } | |
| 1887 | +#else | |
| 1888 | + fossil_trace("db_write_saved_encryption_key_to_process unsupported"); | |
| 1889 | +#endif | |
| 1890 | + } | |
| 1891 | +} | |
| 1892 | + | |
| 1625 | 1893 | /* |
| 1626 | -** This function sets the saved database encryption key to one that gets | |
| 1627 | -** read from the specified Fossil parent process. This is only necessary | |
| 1628 | -** (or functional) on Windows. | |
| 1894 | +** This function zeros the saved database encryption key in the specified | |
| 1895 | +** Fossil parent process. This is only necessary (or functional) on | |
| 1896 | +** Windows or Linux. | |
| 1629 | 1897 | */ |
| 1630 | -void db_read_saved_encryption_key_from_process( | |
| 1631 | - DWORD processId, /* Identifier for Fossil parent process. */ | |
| 1898 | +static void db_zero_saved_encryption_key_in_process( | |
| 1899 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1632 | 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | 1902 | ){ |
| 1635 | 1903 | void *p = NULL; |
| 1636 | 1904 | size_t n = 0; |
| 1637 | 1905 | size_t pageSize = 0; |
| 1638 | - HANDLE hProcess = NULL; | |
| 1639 | 1906 | |
| 1640 | 1907 | fossil_get_page_size(&pageSize); |
| 1641 | 1908 | assert( pageSize>0 ); |
| 1642 | 1909 | if( nSize>pageSize ){ |
| 1643 | 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | ||
| 1644 | 1911 | } |
| 1645 | 1912 | p = fossil_secure_alloc_page(&n); |
| 1646 | 1913 | assert( p!=NULL ); |
| 1647 | 1914 | assert( n==pageSize ); |
| 1648 | 1915 | assert( n>=nSize ); |
| 1649 | - hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); | |
| 1650 | - if( hProcess!=NULL ){ | |
| 1651 | - SIZE_T nRead = 0; | |
| 1652 | - if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ | |
| 1653 | - CloseHandle(hProcess); | |
| 1654 | - if( nRead==nSize ){ | |
| 1655 | - db_unsave_encryption_key(); | |
| 1656 | - zSavedKey = p; | |
| 1657 | - savedKeySize = n; | |
| 1658 | - }else{ | |
| 1659 | - fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", | |
| 1660 | - nRead, nSize, pAddress, processId); | |
| 1661 | - } | |
| 1662 | - }else{ | |
| 1663 | - CloseHandle(hProcess); | |
| 1664 | - fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1665 | - pAddress, processId, GetLastError()); | |
| 1666 | - } | |
| 1667 | - }else{ | |
| 1668 | - fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1916 | + { | |
| 1917 | +#if defined(_WIN32) | |
| 1918 | + HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, | |
| 1919 | + FALSE, processId); | |
| 1920 | + if( hProcess!=NULL ){ | |
| 1921 | + SIZE_T nWrite = 0; | |
| 1922 | + if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ | |
| 1923 | + CloseHandle(hProcess); | |
| 1924 | + fossil_secure_free_page(p, n); | |
| 1925 | + if( nWrite!=nSize ){ | |
| 1926 | + fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu", | |
| 1927 | + nWrite, nSize, pAddress, processId); | |
| 1928 | + } | |
| 1929 | + }else{ | |
| 1930 | + CloseHandle(hProcess); | |
| 1931 | + fossil_secure_free_page(p, n); | |
| 1932 | + fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1933 | + pAddress, processId, GetLastError()); | |
| 1934 | + } | |
| 1935 | + }else{ | |
| 1936 | + fossil_secure_free_page(p, n); | |
| 1937 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1938 | + } | |
| 1939 | +#elif defined(__linux__) | |
| 1940 | + ssize_t nWrite; | |
| 1941 | + struct iovec liov = {0}; | |
| 1942 | + struct iovec riov = {0}; | |
| 1943 | + liov.iov_base = p; | |
| 1944 | + liov.iov_len = n; | |
| 1945 | + riov.iov_base = pAddress; | |
| 1946 | + riov.iov_len = nSize; | |
| 1947 | + nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); | |
| 1948 | + if( nWrite!=nSize ){ | |
| 1949 | + fossil_secure_free_page(p, n); | |
| 1950 | + fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu", | |
| 1951 | + nWrite, nSize, pAddress, (unsigned long)processId); | |
| 1952 | + } | |
| 1953 | +#else | |
| 1954 | + fossil_secure_free_page(p, n); | |
| 1955 | + fossil_trace("db_zero_saved_encryption_key_in_process unsupported"); | |
| 1956 | +#endif | |
| 1669 | 1957 | } |
| 1670 | 1958 | } |
| 1671 | 1959 | |
| 1672 | 1960 | /* |
| 1673 | 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | 1963 | ** address, and size (in bytes) of the database encryption key. This is |
| 1676 | -** only necessary (or functional) on Windows. | |
| 1964 | +** only necessary (or functional) on Windows or Linux. | |
| 1677 | 1965 | */ |
| 1678 | -void db_read_saved_encryption_key_from_process_via_th1( | |
| 1679 | - const char *zConfig /* The TH1 script to evaluate. */ | |
| 1966 | +static PID_T db_handle_saved_encryption_key_for_process_via_th1( | |
| 1967 | + const char *zConfig, /* The TH1 script to evaluate. */ | |
| 1968 | + int eType /* Non-zero to write key to parent process -OR- | |
| 1969 | + * zero to read it from the parent process. */ | |
| 1680 | 1970 | ){ |
| 1971 | + PID_T processId = 0; | |
| 1681 | 1972 | int rc; |
| 1682 | 1973 | char *zResult; |
| 1683 | 1974 | char *zPwd = file_getcwd(0, 0); |
| 1684 | 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | ||
| 1686 | 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | 1978 | if( rc!=TH_OK ){ |
| 1688 | 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | 1980 | } |
| 1690 | 1981 | if( zResult ){ |
| 1691 | - DWORD processId = 0; | |
| 1692 | 1982 | LPVOID pAddress = NULL; |
| 1693 | 1983 | SIZE_T nSize = 0; |
| 1694 | 1984 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1695 | - db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 1985 | + if( eType==SEE_KEY_READ ){ | |
| 1986 | + db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 1987 | + }else if( eType==SEE_KEY_WRITE ){ | |
| 1988 | + db_write_saved_encryption_key_to_process(processId, pAddress, nSize); | |
| 1989 | + }else if( eType==SEE_KEY_ZERO ){ | |
| 1990 | + db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); | |
| 1991 | + }else{ | |
| 1992 | + fossil_panic("unsupported SEE key operation %d", eType); | |
| 1993 | + } | |
| 1696 | 1994 | } |
| 1697 | 1995 | file_chdir(zPwd, 0); |
| 1698 | 1996 | fossil_free(zPwd); |
| 1997 | + return processId; | |
| 1699 | 1998 | } |
| 1700 | -#endif /* defined(_WIN32) */ | |
| 1999 | + | |
| 2000 | +/* | |
| 2001 | +** This function sets the saved database encryption key to one that gets | |
| 2002 | +** read from the specified Fossil parent process, if applicable. This is | |
| 2003 | +** only necessary (or functional) on Windows or Linux. | |
| 2004 | +*/ | |
| 2005 | +PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){ | |
| 2006 | + PID_T processId = 0; | |
| 2007 | + g.zPidKey = find_option("usepidkey",0,1); | |
| 2008 | + if( !g.zPidKey ){ | |
| 2009 | + g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY"); | |
| 2010 | + } | |
| 2011 | + if( g.zPidKey ){ | |
| 2012 | + LPVOID pAddress = NULL; | |
| 2013 | + SIZE_T nSize = 0; | |
| 2014 | + parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); | |
| 2015 | + if( eType==SEE_KEY_READ ){ | |
| 2016 | + db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 2017 | + }else if( eType==SEE_KEY_WRITE ){ | |
| 2018 | + db_write_saved_encryption_key_to_process(processId, pAddress, nSize); | |
| 2019 | + }else if( eType==SEE_KEY_ZERO ){ | |
| 2020 | + db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); | |
| 2021 | + }else{ | |
| 2022 | + fossil_panic("unsupported SEE key operation %d", eType); | |
| 2023 | + } | |
| 2024 | + }else{ | |
| 2025 | + const char *zSeeDbConfig = find_option("seedbcfg",0,1); | |
| 2026 | + if( !zSeeDbConfig ){ | |
| 2027 | + zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); | |
| 2028 | + } | |
| 2029 | + if( zSeeDbConfig ){ | |
| 2030 | + processId = db_handle_saved_encryption_key_for_process_via_th1( | |
| 2031 | + zSeeDbConfig, eType | |
| 2032 | + ); | |
| 2033 | + } | |
| 2034 | + } | |
| 2035 | + return processId; | |
| 2036 | +} | |
| 1701 | 2037 | #endif /* USE_SEE */ |
| 1702 | 2038 | |
| 1703 | 2039 | /* |
| 1704 | 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | 2041 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | ||
| 3294 | 3630 | noWarn = 1; |
| 3295 | 3631 | } |
| 3296 | 3632 | } |
| 3297 | 3633 | blob_reset(&versionedPathname); |
| 3298 | 3634 | if( found ){ |
| 3635 | + blob_strip_comment_lines(&setting, &setting); | |
| 3299 | 3636 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3300 | 3637 | ** on boolean properties */ |
| 3301 | 3638 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3302 | 3639 | } |
| 3303 | 3640 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | ||
| 4158 | 4495 | ** every time the backoffice runs. This can be used for debugging, |
| 4159 | 4496 | ** to ensure that backoffice is running appropriately. |
| 4160 | 4497 | */ |
| 4161 | 4498 | /* |
| 4162 | 4499 | ** SETTING: binary-glob width=40 versionable block-text |
| 4163 | -** The VALUE of this setting is a comma or newline-separated list of | |
| 4164 | -** GLOB patterns that should be treated as binary files | |
| 4165 | -** for committing and merging purposes. Example: *.jpg | |
| 4500 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4501 | +** that should be treated as "binary" for committing and merging | |
| 4502 | +** purposes. Example: *.jpg,*.png The parsing rules are complex; | |
| 4503 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4166 | 4504 | */ |
| 4167 | 4505 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4168 | 4506 | /* |
| 4169 | 4507 | ** SETTING: case-sensitive boolean default=off |
| 4170 | 4508 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | ||
| 4182 | 4520 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4183 | 4521 | */ |
| 4184 | 4522 | #endif |
| 4185 | 4523 | /* |
| 4186 | 4524 | ** SETTING: clean-glob width=40 versionable block-text |
| 4187 | -** The VALUE of this setting is a comma or newline-separated list of GLOB | |
| 4188 | -** patterns specifying files that the "clean" command will | |
| 4189 | -** delete without prompting or allowing undo. | |
| 4190 | -** Example: *.a,*.lib,*.o | |
| 4525 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4526 | +** that the "clean" command will delete without prompting or allowing | |
| 4527 | +** undo. Example: *.a,*.o,*.so The parsing rules are complex; | |
| 4528 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4191 | 4529 | */ |
| 4192 | 4530 | /* |
| 4193 | 4531 | ** SETTING: clearsign boolean default=off |
| 4194 | 4532 | ** When enabled, fossil will attempt to sign all commits |
| 4195 | 4533 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | ||
| 4217 | 4555 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4218 | 4556 | ** configured through the /setup_timeline web page. |
| 4219 | 4557 | */ |
| 4220 | 4558 | /* |
| 4221 | 4559 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4222 | -** The value is a comma or newline-separated list of GLOB patterns for | |
| 4223 | -** text files in which it is ok to have CR, CR+LF or mixed | |
| 4224 | -** line endings. Set to "*" to disable CR+LF checking. | |
| 4560 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4561 | +** in which it is allowed to have CR, CR+LF or mixed line endings, | |
| 4562 | +** suppressing Fossil's normal warning about this. Set it to "*" to | |
| 4563 | +** disable CR+LF checking entirely. Example: *.md,*.txt | |
| 4225 | 4564 | ** The crnl-glob setting is a compatibility alias. |
| 4226 | 4565 | */ |
| 4227 | 4566 | /* |
| 4228 | 4567 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4229 | 4568 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | ||
| 4265 | 4604 | ** The value is an external command that will launch the |
| 4266 | 4605 | ** text editor command used for check-in comments. |
| 4267 | 4606 | */ |
| 4268 | 4607 | /* |
| 4269 | 4608 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4270 | -** The value is a comma or newline-separated list of pathnames. On | |
| 4271 | -** update and checkout commands, if no file or directory | |
| 4272 | -** exists with that name, an empty directory will be | |
| 4273 | -** created. | |
| 4609 | +** The value is a list of pathnames parsed according to the same rules as | |
| 4610 | +** the *-glob settings. On update and checkout commands, if no directory | |
| 4611 | +** exists with that name, an empty directory will be be created, even if | |
| 4612 | +** it must create one or more parent directories. | |
| 4274 | 4613 | */ |
| 4275 | 4614 | /* |
| 4276 | 4615 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4277 | -** The value is a comma or newline-separated list of GLOB | |
| 4278 | -** patterns specifying files that the "commit" command will | |
| 4279 | -** ignore when issuing warnings about text files that may | |
| 4280 | -** use another encoding than ASCII or UTF-8. Set to "*" | |
| 4281 | -** to disable encoding checking. | |
| 4616 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4617 | +** the "commit" command will ignore when issuing warnings about text files | |
| 4618 | +** that may use another encoding than ASCII or UTF-8. Set to "*" to disable | |
| 4619 | +** encoding checking. Example: *.md,*.txt The parsing rules are complex; | |
| 4620 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4282 | 4621 | */ |
| 4283 | 4622 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4284 | 4623 | /* |
| 4285 | 4624 | ** SETTING: exec-rel-paths boolean default=on |
| 4286 | 4625 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | ||
| 4295 | 4634 | */ |
| 4296 | 4635 | #endif |
| 4297 | 4636 | |
| 4298 | 4637 | /* |
| 4299 | 4638 | ** SETTING: fileedit-glob width=40 block-text |
| 4300 | -** A comma- or newline-separated list of globs of filenames | |
| 4301 | -** which are allowed to be edited using the /fileedit page. | |
| 4302 | -** An empty list prohibits editing via that page. Note that | |
| 4303 | -** it cannot edit binary files, so the list should not | |
| 4639 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4640 | +** which are allowed to be edited using the /fileedit page. An empty list | |
| 4641 | +** suppresses the feature. Example: *.md,*.txt The parsing rules are | |
| 4642 | +** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4643 | +** Note that /fileedit cannot edit binary files, so the list should not | |
| 4304 | 4644 | ** contain any globs for, e.g., images or PDFs. |
| 4305 | 4645 | */ |
| 4306 | 4646 | /* |
| 4307 | 4647 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4308 | 4648 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | ||
| 4340 | 4680 | ** If true, then the Fossil web server will redirect unencrypted |
| 4341 | 4681 | ** login screen requests to HTTPS. |
| 4342 | 4682 | */ |
| 4343 | 4683 | /* |
| 4344 | 4684 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4345 | -** The value is a list of GLOB patterns, separated by spaces, | |
| 4346 | -** commas, or newlines, specifying files that the "add", | |
| 4347 | -** "addremove", "clean", and "extras" commands will ignore. | |
| 4348 | -** | |
| 4349 | -** Example: *.log, customCode.c, notes.txt | |
| 4685 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4686 | +** the "add", "addremove", "clean", and "extras" commands will ignore. | |
| 4687 | +** Example: *.log,notes.txt The parsing rules are complex; see | |
| 4688 | +** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4350 | 4689 | */ |
| 4351 | 4690 | /* |
| 4352 | 4691 | ** SETTING: keep-glob width=40 versionable block-text |
| 4353 | -** The value is list of GLOB patterns, separated by spaces, | |
| 4354 | -** commas, or newlines, specifying files that the "clean" | |
| 4355 | -** command will keep. | |
| 4692 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4693 | +** the "clean" command must not delete. Example: build/precious.exe | |
| 4694 | +** The parsing rules are complex; see | |
| 4695 | +** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4356 | 4696 | */ |
| 4357 | 4697 | /* |
| 4358 | 4698 | ** SETTING: localauth boolean default=off |
| 4359 | 4699 | ** If enabled, require that HTTP connections from the loopback |
| 4360 | 4700 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4361 | 4701 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -31,25 +31,57 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #if defined(_WIN32) |
| 34 | # if USE_SEE |
| 35 | # include <windows.h> |
| 36 | # endif |
| 37 | #else |
| 38 | # include <pwd.h> |
| 39 | #endif |
| 40 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | # define SQLITE_HAS_CODEC |
| 42 | #endif |
| 43 | #include <sqlite3.h> |
| 44 | #include <sys/types.h> |
| 45 | #include <sys/stat.h> |
| 46 | #include <unistd.h> |
| 47 | #include <time.h> |
| 48 | #include "db.h" |
| 49 | |
| 50 | #if INTERFACE |
| 51 | /* |
| 52 | ** An single SQL statement is represented as an instance of the following |
| 53 | ** structure. |
| 54 | */ |
| 55 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | |
| 1539 | static char *zSavedKey = 0; |
| 1540 | |
| 1541 | /* |
| 1542 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | */ |
| 1544 | size_t savedKeySize = 0; |
| 1545 | |
| 1546 | /* |
| 1547 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | ** no database encryption key is saved. |
| 1549 | */ |
| @@ -1556,10 +1607,36 @@ | |
| 1556 | ** -OR- zero if no database encryption key is saved. |
| 1557 | */ |
| 1558 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | return savedKeySize; |
| 1560 | } |
| 1561 | |
| 1562 | /* |
| 1563 | ** This function arranges for the database encryption key to be securely |
| 1564 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | */ |
| @@ -1569,10 +1646,11 @@ | |
| 1569 | void *p = NULL; |
| 1570 | size_t n = 0; |
| 1571 | size_t pageSize = 0; |
| 1572 | size_t blobSize = 0; |
| 1573 | |
| 1574 | blobSize = blob_size(pKey); |
| 1575 | if( blobSize==0 ) return; |
| 1576 | fossil_get_page_size(&pageSize); |
| 1577 | assert( pageSize>0 ); |
| 1578 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | |
| 1599 | |
| 1600 | /* |
| 1601 | ** This function sets the saved database encryption key to the specified |
| 1602 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | */ |
| 1604 | void db_set_saved_encryption_key( |
| 1605 | Blob *pKey |
| 1606 | ){ |
| 1607 | if( zSavedKey!=NULL ){ |
| 1608 | size_t blobSize = blob_size(pKey); |
| 1609 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | |
| 1619 | }else{ |
| 1620 | db_save_encryption_key(pKey); |
| 1621 | } |
| 1622 | } |
| 1623 | |
| 1624 | #if defined(_WIN32) |
| 1625 | /* |
| 1626 | ** This function sets the saved database encryption key to one that gets |
| 1627 | ** read from the specified Fossil parent process. This is only necessary |
| 1628 | ** (or functional) on Windows. |
| 1629 | */ |
| 1630 | void db_read_saved_encryption_key_from_process( |
| 1631 | DWORD processId, /* Identifier for Fossil parent process. */ |
| 1632 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | ){ |
| 1635 | void *p = NULL; |
| 1636 | size_t n = 0; |
| 1637 | size_t pageSize = 0; |
| 1638 | HANDLE hProcess = NULL; |
| 1639 | |
| 1640 | fossil_get_page_size(&pageSize); |
| 1641 | assert( pageSize>0 ); |
| 1642 | if( nSize>pageSize ){ |
| 1643 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | |
| 1644 | } |
| 1645 | p = fossil_secure_alloc_page(&n); |
| 1646 | assert( p!=NULL ); |
| 1647 | assert( n==pageSize ); |
| 1648 | assert( n>=nSize ); |
| 1649 | hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); |
| 1650 | if( hProcess!=NULL ){ |
| 1651 | SIZE_T nRead = 0; |
| 1652 | if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ |
| 1653 | CloseHandle(hProcess); |
| 1654 | if( nRead==nSize ){ |
| 1655 | db_unsave_encryption_key(); |
| 1656 | zSavedKey = p; |
| 1657 | savedKeySize = n; |
| 1658 | }else{ |
| 1659 | fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", |
| 1660 | nRead, nSize, pAddress, processId); |
| 1661 | } |
| 1662 | }else{ |
| 1663 | CloseHandle(hProcess); |
| 1664 | fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, |
| 1665 | pAddress, processId, GetLastError()); |
| 1666 | } |
| 1667 | }else{ |
| 1668 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1669 | } |
| 1670 | } |
| 1671 | |
| 1672 | /* |
| 1673 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | ** address, and size (in bytes) of the database encryption key. This is |
| 1676 | ** only necessary (or functional) on Windows. |
| 1677 | */ |
| 1678 | void db_read_saved_encryption_key_from_process_via_th1( |
| 1679 | const char *zConfig /* The TH1 script to evaluate. */ |
| 1680 | ){ |
| 1681 | int rc; |
| 1682 | char *zResult; |
| 1683 | char *zPwd = file_getcwd(0, 0); |
| 1684 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | |
| 1686 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | if( rc!=TH_OK ){ |
| 1688 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | } |
| 1690 | if( zResult ){ |
| 1691 | DWORD processId = 0; |
| 1692 | LPVOID pAddress = NULL; |
| 1693 | SIZE_T nSize = 0; |
| 1694 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1695 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 1696 | } |
| 1697 | file_chdir(zPwd, 0); |
| 1698 | fossil_free(zPwd); |
| 1699 | } |
| 1700 | #endif /* defined(_WIN32) */ |
| 1701 | #endif /* USE_SEE */ |
| 1702 | |
| 1703 | /* |
| 1704 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | |
| 3294 | noWarn = 1; |
| 3295 | } |
| 3296 | } |
| 3297 | blob_reset(&versionedPathname); |
| 3298 | if( found ){ |
| 3299 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3300 | ** on boolean properties */ |
| 3301 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3302 | } |
| 3303 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | |
| 4158 | ** every time the backoffice runs. This can be used for debugging, |
| 4159 | ** to ensure that backoffice is running appropriately. |
| 4160 | */ |
| 4161 | /* |
| 4162 | ** SETTING: binary-glob width=40 versionable block-text |
| 4163 | ** The VALUE of this setting is a comma or newline-separated list of |
| 4164 | ** GLOB patterns that should be treated as binary files |
| 4165 | ** for committing and merging purposes. Example: *.jpg |
| 4166 | */ |
| 4167 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4168 | /* |
| 4169 | ** SETTING: case-sensitive boolean default=off |
| 4170 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | |
| 4182 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4183 | */ |
| 4184 | #endif |
| 4185 | /* |
| 4186 | ** SETTING: clean-glob width=40 versionable block-text |
| 4187 | ** The VALUE of this setting is a comma or newline-separated list of GLOB |
| 4188 | ** patterns specifying files that the "clean" command will |
| 4189 | ** delete without prompting or allowing undo. |
| 4190 | ** Example: *.a,*.lib,*.o |
| 4191 | */ |
| 4192 | /* |
| 4193 | ** SETTING: clearsign boolean default=off |
| 4194 | ** When enabled, fossil will attempt to sign all commits |
| 4195 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | |
| 4217 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4218 | ** configured through the /setup_timeline web page. |
| 4219 | */ |
| 4220 | /* |
| 4221 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4222 | ** The value is a comma or newline-separated list of GLOB patterns for |
| 4223 | ** text files in which it is ok to have CR, CR+LF or mixed |
| 4224 | ** line endings. Set to "*" to disable CR+LF checking. |
| 4225 | ** The crnl-glob setting is a compatibility alias. |
| 4226 | */ |
| 4227 | /* |
| 4228 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4229 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | |
| 4265 | ** The value is an external command that will launch the |
| 4266 | ** text editor command used for check-in comments. |
| 4267 | */ |
| 4268 | /* |
| 4269 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4270 | ** The value is a comma or newline-separated list of pathnames. On |
| 4271 | ** update and checkout commands, if no file or directory |
| 4272 | ** exists with that name, an empty directory will be |
| 4273 | ** created. |
| 4274 | */ |
| 4275 | /* |
| 4276 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4277 | ** The value is a comma or newline-separated list of GLOB |
| 4278 | ** patterns specifying files that the "commit" command will |
| 4279 | ** ignore when issuing warnings about text files that may |
| 4280 | ** use another encoding than ASCII or UTF-8. Set to "*" |
| 4281 | ** to disable encoding checking. |
| 4282 | */ |
| 4283 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4284 | /* |
| 4285 | ** SETTING: exec-rel-paths boolean default=on |
| 4286 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | |
| 4295 | */ |
| 4296 | #endif |
| 4297 | |
| 4298 | /* |
| 4299 | ** SETTING: fileedit-glob width=40 block-text |
| 4300 | ** A comma- or newline-separated list of globs of filenames |
| 4301 | ** which are allowed to be edited using the /fileedit page. |
| 4302 | ** An empty list prohibits editing via that page. Note that |
| 4303 | ** it cannot edit binary files, so the list should not |
| 4304 | ** contain any globs for, e.g., images or PDFs. |
| 4305 | */ |
| 4306 | /* |
| 4307 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4308 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | |
| 4340 | ** If true, then the Fossil web server will redirect unencrypted |
| 4341 | ** login screen requests to HTTPS. |
| 4342 | */ |
| 4343 | /* |
| 4344 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4345 | ** The value is a list of GLOB patterns, separated by spaces, |
| 4346 | ** commas, or newlines, specifying files that the "add", |
| 4347 | ** "addremove", "clean", and "extras" commands will ignore. |
| 4348 | ** |
| 4349 | ** Example: *.log, customCode.c, notes.txt |
| 4350 | */ |
| 4351 | /* |
| 4352 | ** SETTING: keep-glob width=40 versionable block-text |
| 4353 | ** The value is list of GLOB patterns, separated by spaces, |
| 4354 | ** commas, or newlines, specifying files that the "clean" |
| 4355 | ** command will keep. |
| 4356 | */ |
| 4357 | /* |
| 4358 | ** SETTING: localauth boolean default=off |
| 4359 | ** If enabled, require that HTTP connections from the loopback |
| 4360 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4361 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -31,25 +31,57 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #if defined(_WIN32) |
| 34 | # if USE_SEE |
| 35 | # include <windows.h> |
| 36 | # define GETPID (int)GetCurrentProcessId |
| 37 | # endif |
| 38 | #else |
| 39 | # include <pwd.h> |
| 40 | # if USE_SEE |
| 41 | # define GETPID getpid |
| 42 | # endif |
| 43 | #endif |
| 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 45 | # define SQLITE_HAS_CODEC |
| 46 | #endif |
| 47 | #if USE_SEE && defined(__linux__) |
| 48 | # include <sys/uio.h> |
| 49 | #endif |
| 50 | #include <sqlite3.h> |
| 51 | #include <sys/types.h> |
| 52 | #include <sys/stat.h> |
| 53 | #include <unistd.h> |
| 54 | #include <time.h> |
| 55 | |
| 56 | /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ |
| 57 | #if USE_SEE |
| 58 | #if defined(_WIN32) |
| 59 | typedef DWORD PID_T; |
| 60 | #else |
| 61 | typedef pid_t PID_T; |
| 62 | #endif |
| 63 | #endif |
| 64 | |
| 65 | #include "db.h" |
| 66 | |
| 67 | #if INTERFACE |
| 68 | /* |
| 69 | ** Type definitions used for handling the saved encryption key for SEE. |
| 70 | */ |
| 71 | #if !defined(_WIN32) |
| 72 | typedef void *LPVOID; |
| 73 | typedef size_t SIZE_T; |
| 74 | #endif |
| 75 | |
| 76 | /* |
| 77 | ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al. |
| 78 | */ |
| 79 | #define SEE_KEY_READ ((int)0) |
| 80 | #define SEE_KEY_WRITE ((int)1) |
| 81 | #define SEE_KEY_ZERO ((int)2) |
| 82 | |
| 83 | /* |
| 84 | ** An single SQL statement is represented as an instance of the following |
| 85 | ** structure. |
| 86 | */ |
| 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | |
| 1571 | static char *zSavedKey = 0; |
| 1572 | |
| 1573 | /* |
| 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1575 | */ |
| 1576 | static size_t savedKeySize = 0; |
| 1577 | |
| 1578 | /* |
| 1579 | ** This function returns non-zero if there is a saved database encryption |
| 1580 | ** key available. |
| 1581 | */ |
| 1582 | int db_have_saved_encryption_key(){ |
| 1583 | return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize); |
| 1584 | } |
| 1585 | |
| 1586 | /* |
| 1587 | ** This function returns non-zero if the specified database encryption key |
| 1588 | ** is valid. |
| 1589 | */ |
| 1590 | int db_is_valid_saved_encryption_key(const char *p, size_t n){ |
| 1591 | if( p==0 ) return 0; |
| 1592 | if( n==0 ) return 0; |
| 1593 | if( p[0]==0 ) return 0; |
| 1594 | return 1; |
| 1595 | } |
| 1596 | |
| 1597 | /* |
| 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1599 | ** no database encryption key is saved. |
| 1600 | */ |
| @@ -1556,10 +1607,36 @@ | |
| 1607 | ** -OR- zero if no database encryption key is saved. |
| 1608 | */ |
| 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1610 | return savedKeySize; |
| 1611 | } |
| 1612 | |
| 1613 | /* |
| 1614 | ** This function arranges for the saved database encryption key buffer |
| 1615 | ** to be allocated and then sets up the environment variable to allow |
| 1616 | ** a child process to initialize it with the actual database encryption |
| 1617 | ** key. |
| 1618 | */ |
| 1619 | void db_setup_for_saved_encryption_key(){ |
| 1620 | void *p = NULL; |
| 1621 | size_t n = 0; |
| 1622 | size_t pageSize = 0; |
| 1623 | Blob pidKey; |
| 1624 | |
| 1625 | assert( !db_have_saved_encryption_key() ); |
| 1626 | db_unsave_encryption_key(); |
| 1627 | fossil_get_page_size(&pageSize); |
| 1628 | assert( pageSize>0 ); |
| 1629 | p = fossil_secure_alloc_page(&n); |
| 1630 | assert( p!=NULL ); |
| 1631 | assert( n==pageSize ); |
| 1632 | blob_zero(&pidKey); |
| 1633 | blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n); |
| 1634 | fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey)); |
| 1635 | zSavedKey = p; |
| 1636 | savedKeySize = n; |
| 1637 | } |
| 1638 | |
| 1639 | /* |
| 1640 | ** This function arranges for the database encryption key to be securely |
| 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1642 | */ |
| @@ -1569,10 +1646,11 @@ | |
| 1646 | void *p = NULL; |
| 1647 | size_t n = 0; |
| 1648 | size_t pageSize = 0; |
| 1649 | size_t blobSize = 0; |
| 1650 | |
| 1651 | assert( !db_have_saved_encryption_key() ); |
| 1652 | blobSize = blob_size(pKey); |
| 1653 | if( blobSize==0 ) return; |
| 1654 | fossil_get_page_size(&pageSize); |
| 1655 | assert( pageSize>0 ); |
| 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | |
| 1677 | |
| 1678 | /* |
| 1679 | ** This function sets the saved database encryption key to the specified |
| 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1681 | */ |
| 1682 | static void db_set_saved_encryption_key( |
| 1683 | Blob *pKey |
| 1684 | ){ |
| 1685 | if( zSavedKey!=NULL ){ |
| 1686 | size_t blobSize = blob_size(pKey); |
| 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | |
| 1697 | }else{ |
| 1698 | db_save_encryption_key(pKey); |
| 1699 | } |
| 1700 | } |
| 1701 | |
| 1702 | /* |
| 1703 | ** WEBPAGE: setseekey |
| 1704 | ** |
| 1705 | ** Sets the sets the saved database encryption key to one that gets passed |
| 1706 | ** via the "key" query string parameter. If the saved database encryption |
| 1707 | ** key has already been set, does nothing. This web page does not produce |
| 1708 | ** any output on success or failure. No permissions are required and none |
| 1709 | ** are checked (partially due to lack of encrypted database access). |
| 1710 | ** |
| 1711 | ** Query parameters: |
| 1712 | ** |
| 1713 | ** key The string to set as the saved database encryption |
| 1714 | ** key. |
| 1715 | */ |
| 1716 | void db_set_see_key_page(void){ |
| 1717 | Blob key; |
| 1718 | const char *zKey; |
| 1719 | if( db_have_saved_encryption_key() ){ |
| 1720 | fossil_trace("SEE: encryption key was already set\n"); |
| 1721 | return; |
| 1722 | } |
| 1723 | zKey = P("key"); |
| 1724 | blob_init(&key, 0, 0); |
| 1725 | if( zKey!=0 ){ |
| 1726 | PID_T processId; |
| 1727 | blob_set(&key, zKey); |
| 1728 | db_set_saved_encryption_key(&key); |
| 1729 | processId = db_maybe_handle_saved_encryption_key_for_process( |
| 1730 | SEE_KEY_WRITE |
| 1731 | ); |
| 1732 | fossil_trace("SEE: set encryption key for process %lu, length %u\n", |
| 1733 | (unsigned long)processId, blob_size(&key)); |
| 1734 | }else{ |
| 1735 | fossil_trace("SEE: no encryption key specified\n"); |
| 1736 | } |
| 1737 | blob_reset(&key); |
| 1738 | } |
| 1739 | |
| 1740 | /* |
| 1741 | ** WEBPAGE: unsetseekey |
| 1742 | ** |
| 1743 | ** Sets the saved database encryption key to zeros in the current and parent |
| 1744 | ** Fossil processes. This web page does not produce any output on success |
| 1745 | ** or failure. Setup permission is required. |
| 1746 | */ |
| 1747 | void db_unset_see_key_page(void){ |
| 1748 | PID_T processId; |
| 1749 | login_check_credentials(); |
| 1750 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 1751 | processId = db_maybe_handle_saved_encryption_key_for_process( |
| 1752 | SEE_KEY_ZERO |
| 1753 | ); |
| 1754 | fossil_trace("SEE: unset encryption key for process %lu\n", |
| 1755 | (unsigned long)processId); |
| 1756 | } |
| 1757 | |
| 1758 | /* |
| 1759 | ** This function reads the saved database encryption key from the |
| 1760 | ** specified Fossil parent process. This is only necessary (or |
| 1761 | ** functional) on Windows or Linux. |
| 1762 | */ |
| 1763 | static void db_read_saved_encryption_key_from_process( |
| 1764 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1765 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1766 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1767 | ){ |
| 1768 | void *p = NULL; |
| 1769 | size_t n = 0; |
| 1770 | size_t pageSize = 0; |
| 1771 | |
| 1772 | fossil_get_page_size(&pageSize); |
| 1773 | assert( pageSize>0 ); |
| 1774 | if( nSize>pageSize ){ |
| 1775 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| 1776 | } |
| 1777 | p = fossil_secure_alloc_page(&n); |
| 1778 | assert( p!=NULL ); |
| 1779 | assert( n==pageSize ); |
| 1780 | assert( n>=nSize ); |
| 1781 | { |
| 1782 | #if defined(_WIN32) |
| 1783 | HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); |
| 1784 | if( hProcess!=NULL ){ |
| 1785 | SIZE_T nRead = 0; |
| 1786 | if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ |
| 1787 | CloseHandle(hProcess); |
| 1788 | if( nRead==nSize ){ |
| 1789 | db_unsave_encryption_key(); |
| 1790 | zSavedKey = p; |
| 1791 | savedKeySize = n; |
| 1792 | }else{ |
| 1793 | fossil_secure_free_page(p, n); |
| 1794 | fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", |
| 1795 | nRead, nSize, pAddress, processId); |
| 1796 | } |
| 1797 | }else{ |
| 1798 | CloseHandle(hProcess); |
| 1799 | fossil_secure_free_page(p, n); |
| 1800 | fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, |
| 1801 | pAddress, processId, GetLastError()); |
| 1802 | } |
| 1803 | }else{ |
| 1804 | fossil_secure_free_page(p, n); |
| 1805 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1806 | } |
| 1807 | #elif defined(__linux__) |
| 1808 | ssize_t nRead; |
| 1809 | struct iovec liov = {0}; |
| 1810 | struct iovec riov = {0}; |
| 1811 | liov.iov_base = p; |
| 1812 | liov.iov_len = n; |
| 1813 | riov.iov_base = pAddress; |
| 1814 | riov.iov_len = nSize; |
| 1815 | nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0); |
| 1816 | if( nRead==nSize ){ |
| 1817 | db_unsave_encryption_key(); |
| 1818 | zSavedKey = p; |
| 1819 | savedKeySize = n; |
| 1820 | }else{ |
| 1821 | fossil_secure_free_page(p, n); |
| 1822 | fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu", |
| 1823 | nRead, nSize, pAddress, (unsigned long)processId); |
| 1824 | } |
| 1825 | #else |
| 1826 | fossil_secure_free_page(p, n); |
| 1827 | fossil_trace("db_read_saved_encryption_key_from_process unsupported"); |
| 1828 | #endif |
| 1829 | } |
| 1830 | } |
| 1831 | |
| 1832 | /* |
| 1833 | ** This function writes the saved database encryption key into the |
| 1834 | ** specified Fossil parent process. This is only necessary (or |
| 1835 | ** functional) on Windows or Linux. |
| 1836 | */ |
| 1837 | static void db_write_saved_encryption_key_to_process( |
| 1838 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1839 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1840 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1841 | ){ |
| 1842 | void *p = db_get_saved_encryption_key(); |
| 1843 | size_t n = db_get_saved_encryption_key_size(); |
| 1844 | size_t pageSize = 0; |
| 1845 | |
| 1846 | fossil_get_page_size(&pageSize); |
| 1847 | assert( pageSize>0 ); |
| 1848 | if( nSize>pageSize ){ |
| 1849 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| 1850 | } |
| 1851 | assert( p!=NULL ); |
| 1852 | assert( n==pageSize ); |
| 1853 | assert( n>=nSize ); |
| 1854 | { |
| 1855 | #if defined(_WIN32) |
| 1856 | HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, |
| 1857 | FALSE, processId); |
| 1858 | if( hProcess!=NULL ){ |
| 1859 | SIZE_T nWrite = 0; |
| 1860 | if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ |
| 1861 | CloseHandle(hProcess); |
| 1862 | if( nWrite!=nSize ){ |
| 1863 | fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu", |
| 1864 | nWrite, nSize, pAddress, processId); |
| 1865 | } |
| 1866 | }else{ |
| 1867 | CloseHandle(hProcess); |
| 1868 | fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize, |
| 1869 | pAddress, processId, GetLastError()); |
| 1870 | } |
| 1871 | }else{ |
| 1872 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1873 | } |
| 1874 | #elif defined(__linux__) |
| 1875 | ssize_t nWrite; |
| 1876 | struct iovec liov = {0}; |
| 1877 | struct iovec riov = {0}; |
| 1878 | liov.iov_base = p; |
| 1879 | liov.iov_len = n; |
| 1880 | riov.iov_base = pAddress; |
| 1881 | riov.iov_len = nSize; |
| 1882 | nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); |
| 1883 | if( nWrite!=nSize ){ |
| 1884 | fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu", |
| 1885 | nWrite, nSize, pAddress, (unsigned long)processId); |
| 1886 | } |
| 1887 | #else |
| 1888 | fossil_trace("db_write_saved_encryption_key_to_process unsupported"); |
| 1889 | #endif |
| 1890 | } |
| 1891 | } |
| 1892 | |
| 1893 | /* |
| 1894 | ** This function zeros the saved database encryption key in the specified |
| 1895 | ** Fossil parent process. This is only necessary (or functional) on |
| 1896 | ** Windows or Linux. |
| 1897 | */ |
| 1898 | static void db_zero_saved_encryption_key_in_process( |
| 1899 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1902 | ){ |
| 1903 | void *p = NULL; |
| 1904 | size_t n = 0; |
| 1905 | size_t pageSize = 0; |
| 1906 | |
| 1907 | fossil_get_page_size(&pageSize); |
| 1908 | assert( pageSize>0 ); |
| 1909 | if( nSize>pageSize ){ |
| 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | |
| 1911 | } |
| 1912 | p = fossil_secure_alloc_page(&n); |
| 1913 | assert( p!=NULL ); |
| 1914 | assert( n==pageSize ); |
| 1915 | assert( n>=nSize ); |
| 1916 | { |
| 1917 | #if defined(_WIN32) |
| 1918 | HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, |
| 1919 | FALSE, processId); |
| 1920 | if( hProcess!=NULL ){ |
| 1921 | SIZE_T nWrite = 0; |
| 1922 | if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ |
| 1923 | CloseHandle(hProcess); |
| 1924 | fossil_secure_free_page(p, n); |
| 1925 | if( nWrite!=nSize ){ |
| 1926 | fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu", |
| 1927 | nWrite, nSize, pAddress, processId); |
| 1928 | } |
| 1929 | }else{ |
| 1930 | CloseHandle(hProcess); |
| 1931 | fossil_secure_free_page(p, n); |
| 1932 | fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize, |
| 1933 | pAddress, processId, GetLastError()); |
| 1934 | } |
| 1935 | }else{ |
| 1936 | fossil_secure_free_page(p, n); |
| 1937 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1938 | } |
| 1939 | #elif defined(__linux__) |
| 1940 | ssize_t nWrite; |
| 1941 | struct iovec liov = {0}; |
| 1942 | struct iovec riov = {0}; |
| 1943 | liov.iov_base = p; |
| 1944 | liov.iov_len = n; |
| 1945 | riov.iov_base = pAddress; |
| 1946 | riov.iov_len = nSize; |
| 1947 | nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); |
| 1948 | if( nWrite!=nSize ){ |
| 1949 | fossil_secure_free_page(p, n); |
| 1950 | fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu", |
| 1951 | nWrite, nSize, pAddress, (unsigned long)processId); |
| 1952 | } |
| 1953 | #else |
| 1954 | fossil_secure_free_page(p, n); |
| 1955 | fossil_trace("db_zero_saved_encryption_key_in_process unsupported"); |
| 1956 | #endif |
| 1957 | } |
| 1958 | } |
| 1959 | |
| 1960 | /* |
| 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1963 | ** address, and size (in bytes) of the database encryption key. This is |
| 1964 | ** only necessary (or functional) on Windows or Linux. |
| 1965 | */ |
| 1966 | static PID_T db_handle_saved_encryption_key_for_process_via_th1( |
| 1967 | const char *zConfig, /* The TH1 script to evaluate. */ |
| 1968 | int eType /* Non-zero to write key to parent process -OR- |
| 1969 | * zero to read it from the parent process. */ |
| 1970 | ){ |
| 1971 | PID_T processId = 0; |
| 1972 | int rc; |
| 1973 | char *zResult; |
| 1974 | char *zPwd = file_getcwd(0, 0); |
| 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | |
| 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1978 | if( rc!=TH_OK ){ |
| 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1980 | } |
| 1981 | if( zResult ){ |
| 1982 | LPVOID pAddress = NULL; |
| 1983 | SIZE_T nSize = 0; |
| 1984 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1985 | if( eType==SEE_KEY_READ ){ |
| 1986 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 1987 | }else if( eType==SEE_KEY_WRITE ){ |
| 1988 | db_write_saved_encryption_key_to_process(processId, pAddress, nSize); |
| 1989 | }else if( eType==SEE_KEY_ZERO ){ |
| 1990 | db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); |
| 1991 | }else{ |
| 1992 | fossil_panic("unsupported SEE key operation %d", eType); |
| 1993 | } |
| 1994 | } |
| 1995 | file_chdir(zPwd, 0); |
| 1996 | fossil_free(zPwd); |
| 1997 | return processId; |
| 1998 | } |
| 1999 | |
| 2000 | /* |
| 2001 | ** This function sets the saved database encryption key to one that gets |
| 2002 | ** read from the specified Fossil parent process, if applicable. This is |
| 2003 | ** only necessary (or functional) on Windows or Linux. |
| 2004 | */ |
| 2005 | PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){ |
| 2006 | PID_T processId = 0; |
| 2007 | g.zPidKey = find_option("usepidkey",0,1); |
| 2008 | if( !g.zPidKey ){ |
| 2009 | g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY"); |
| 2010 | } |
| 2011 | if( g.zPidKey ){ |
| 2012 | LPVOID pAddress = NULL; |
| 2013 | SIZE_T nSize = 0; |
| 2014 | parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); |
| 2015 | if( eType==SEE_KEY_READ ){ |
| 2016 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 2017 | }else if( eType==SEE_KEY_WRITE ){ |
| 2018 | db_write_saved_encryption_key_to_process(processId, pAddress, nSize); |
| 2019 | }else if( eType==SEE_KEY_ZERO ){ |
| 2020 | db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); |
| 2021 | }else{ |
| 2022 | fossil_panic("unsupported SEE key operation %d", eType); |
| 2023 | } |
| 2024 | }else{ |
| 2025 | const char *zSeeDbConfig = find_option("seedbcfg",0,1); |
| 2026 | if( !zSeeDbConfig ){ |
| 2027 | zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); |
| 2028 | } |
| 2029 | if( zSeeDbConfig ){ |
| 2030 | processId = db_handle_saved_encryption_key_for_process_via_th1( |
| 2031 | zSeeDbConfig, eType |
| 2032 | ); |
| 2033 | } |
| 2034 | } |
| 2035 | return processId; |
| 2036 | } |
| 2037 | #endif /* USE_SEE */ |
| 2038 | |
| 2039 | /* |
| 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 2041 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | |
| 3630 | noWarn = 1; |
| 3631 | } |
| 3632 | } |
| 3633 | blob_reset(&versionedPathname); |
| 3634 | if( found ){ |
| 3635 | blob_strip_comment_lines(&setting, &setting); |
| 3636 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3637 | ** on boolean properties */ |
| 3638 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3639 | } |
| 3640 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | |
| 4495 | ** every time the backoffice runs. This can be used for debugging, |
| 4496 | ** to ensure that backoffice is running appropriately. |
| 4497 | */ |
| 4498 | /* |
| 4499 | ** SETTING: binary-glob width=40 versionable block-text |
| 4500 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4501 | ** that should be treated as "binary" for committing and merging |
| 4502 | ** purposes. Example: *.jpg,*.png The parsing rules are complex; |
| 4503 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4504 | */ |
| 4505 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4506 | /* |
| 4507 | ** SETTING: case-sensitive boolean default=off |
| 4508 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | |
| 4520 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4521 | */ |
| 4522 | #endif |
| 4523 | /* |
| 4524 | ** SETTING: clean-glob width=40 versionable block-text |
| 4525 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4526 | ** that the "clean" command will delete without prompting or allowing |
| 4527 | ** undo. Example: *.a,*.o,*.so The parsing rules are complex; |
| 4528 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4529 | */ |
| 4530 | /* |
| 4531 | ** SETTING: clearsign boolean default=off |
| 4532 | ** When enabled, fossil will attempt to sign all commits |
| 4533 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | |
| 4555 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4556 | ** configured through the /setup_timeline web page. |
| 4557 | */ |
| 4558 | /* |
| 4559 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4560 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4561 | ** in which it is allowed to have CR, CR+LF or mixed line endings, |
| 4562 | ** suppressing Fossil's normal warning about this. Set it to "*" to |
| 4563 | ** disable CR+LF checking entirely. Example: *.md,*.txt |
| 4564 | ** The crnl-glob setting is a compatibility alias. |
| 4565 | */ |
| 4566 | /* |
| 4567 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4568 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | |
| 4604 | ** The value is an external command that will launch the |
| 4605 | ** text editor command used for check-in comments. |
| 4606 | */ |
| 4607 | /* |
| 4608 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4609 | ** The value is a list of pathnames parsed according to the same rules as |
| 4610 | ** the *-glob settings. On update and checkout commands, if no directory |
| 4611 | ** exists with that name, an empty directory will be be created, even if |
| 4612 | ** it must create one or more parent directories. |
| 4613 | */ |
| 4614 | /* |
| 4615 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4616 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4617 | ** the "commit" command will ignore when issuing warnings about text files |
| 4618 | ** that may use another encoding than ASCII or UTF-8. Set to "*" to disable |
| 4619 | ** encoding checking. Example: *.md,*.txt The parsing rules are complex; |
| 4620 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4621 | */ |
| 4622 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4623 | /* |
| 4624 | ** SETTING: exec-rel-paths boolean default=on |
| 4625 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | |
| 4634 | */ |
| 4635 | #endif |
| 4636 | |
| 4637 | /* |
| 4638 | ** SETTING: fileedit-glob width=40 block-text |
| 4639 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4640 | ** which are allowed to be edited using the /fileedit page. An empty list |
| 4641 | ** suppresses the feature. Example: *.md,*.txt The parsing rules are |
| 4642 | ** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4643 | ** Note that /fileedit cannot edit binary files, so the list should not |
| 4644 | ** contain any globs for, e.g., images or PDFs. |
| 4645 | */ |
| 4646 | /* |
| 4647 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4648 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | |
| 4680 | ** If true, then the Fossil web server will redirect unencrypted |
| 4681 | ** login screen requests to HTTPS. |
| 4682 | */ |
| 4683 | /* |
| 4684 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4685 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4686 | ** the "add", "addremove", "clean", and "extras" commands will ignore. |
| 4687 | ** Example: *.log,notes.txt The parsing rules are complex; see |
| 4688 | ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4689 | */ |
| 4690 | /* |
| 4691 | ** SETTING: keep-glob width=40 versionable block-text |
| 4692 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4693 | ** the "clean" command must not delete. Example: build/precious.exe |
| 4694 | ** The parsing rules are complex; see |
| 4695 | ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4696 | */ |
| 4697 | /* |
| 4698 | ** SETTING: localauth boolean default=off |
| 4699 | ** If enabled, require that HTTP connections from the loopback |
| 4700 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4701 |
M
src/db.c
+405
-65
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -31,25 +31,57 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | #include "config.h" |
| 33 | 33 | #if defined(_WIN32) |
| 34 | 34 | # if USE_SEE |
| 35 | 35 | # include <windows.h> |
| 36 | +# define GETPID (int)GetCurrentProcessId | |
| 36 | 37 | # endif |
| 37 | 38 | #else |
| 38 | 39 | # include <pwd.h> |
| 40 | +# if USE_SEE | |
| 41 | +# define GETPID getpid | |
| 42 | +# endif | |
| 39 | 43 | #endif |
| 40 | 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | 45 | # define SQLITE_HAS_CODEC |
| 46 | +#endif | |
| 47 | +#if USE_SEE && defined(__linux__) | |
| 48 | +# include <sys/uio.h> | |
| 42 | 49 | #endif |
| 43 | 50 | #include <sqlite3.h> |
| 44 | 51 | #include <sys/types.h> |
| 45 | 52 | #include <sys/stat.h> |
| 46 | 53 | #include <unistd.h> |
| 47 | 54 | #include <time.h> |
| 55 | + | |
| 56 | +/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ | |
| 57 | +#if USE_SEE | |
| 58 | +#if defined(_WIN32) | |
| 59 | +typedef DWORD PID_T; | |
| 60 | +#else | |
| 61 | +typedef pid_t PID_T; | |
| 62 | +#endif | |
| 63 | +#endif | |
| 64 | + | |
| 48 | 65 | #include "db.h" |
| 49 | 66 | |
| 50 | 67 | #if INTERFACE |
| 68 | +/* | |
| 69 | +** Type definitions used for handling the saved encryption key for SEE. | |
| 70 | +*/ | |
| 71 | +#if !defined(_WIN32) | |
| 72 | +typedef void *LPVOID; | |
| 73 | +typedef size_t SIZE_T; | |
| 74 | +#endif | |
| 75 | + | |
| 76 | +/* | |
| 77 | +** Operations for db_maybe_handle_saved_encryption_key_for_process, et al. | |
| 78 | +*/ | |
| 79 | +#define SEE_KEY_READ ((int)0) | |
| 80 | +#define SEE_KEY_WRITE ((int)1) | |
| 81 | +#define SEE_KEY_ZERO ((int)2) | |
| 82 | + | |
| 51 | 83 | /* |
| 52 | 84 | ** An single SQL statement is represented as an instance of the following |
| 53 | 85 | ** structure. |
| 54 | 86 | */ |
| 55 | 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | ||
| 1539 | 1571 | static char *zSavedKey = 0; |
| 1540 | 1572 | |
| 1541 | 1573 | /* |
| 1542 | 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | 1575 | */ |
| 1544 | -size_t savedKeySize = 0; | |
| 1576 | +static size_t savedKeySize = 0; | |
| 1577 | + | |
| 1578 | +/* | |
| 1579 | +** This function returns non-zero if there is a saved database encryption | |
| 1580 | +** key available. | |
| 1581 | +*/ | |
| 1582 | +int db_have_saved_encryption_key(){ | |
| 1583 | + return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize); | |
| 1584 | +} | |
| 1585 | + | |
| 1586 | +/* | |
| 1587 | +** This function returns non-zero if the specified database encryption key | |
| 1588 | +** is valid. | |
| 1589 | +*/ | |
| 1590 | +int db_is_valid_saved_encryption_key(const char *p, size_t n){ | |
| 1591 | + if( p==0 ) return 0; | |
| 1592 | + if( n==0 ) return 0; | |
| 1593 | + if( p[0]==0 ) return 0; | |
| 1594 | + return 1; | |
| 1595 | +} | |
| 1545 | 1596 | |
| 1546 | 1597 | /* |
| 1547 | 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | 1599 | ** no database encryption key is saved. |
| 1549 | 1600 | */ |
| @@ -1556,10 +1607,36 @@ | ||
| 1556 | 1607 | ** -OR- zero if no database encryption key is saved. |
| 1557 | 1608 | */ |
| 1558 | 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | 1610 | return savedKeySize; |
| 1560 | 1611 | } |
| 1612 | + | |
| 1613 | +/* | |
| 1614 | +** This function arranges for the saved database encryption key buffer | |
| 1615 | +** to be allocated and then sets up the environment variable to allow | |
| 1616 | +** a child process to initialize it with the actual database encryption | |
| 1617 | +** key. | |
| 1618 | +*/ | |
| 1619 | +void db_setup_for_saved_encryption_key(){ | |
| 1620 | + void *p = NULL; | |
| 1621 | + size_t n = 0; | |
| 1622 | + size_t pageSize = 0; | |
| 1623 | + Blob pidKey; | |
| 1624 | + | |
| 1625 | + assert( !db_have_saved_encryption_key() ); | |
| 1626 | + db_unsave_encryption_key(); | |
| 1627 | + fossil_get_page_size(&pageSize); | |
| 1628 | + assert( pageSize>0 ); | |
| 1629 | + p = fossil_secure_alloc_page(&n); | |
| 1630 | + assert( p!=NULL ); | |
| 1631 | + assert( n==pageSize ); | |
| 1632 | + blob_zero(&pidKey); | |
| 1633 | + blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n); | |
| 1634 | + fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey)); | |
| 1635 | + zSavedKey = p; | |
| 1636 | + savedKeySize = n; | |
| 1637 | +} | |
| 1561 | 1638 | |
| 1562 | 1639 | /* |
| 1563 | 1640 | ** This function arranges for the database encryption key to be securely |
| 1564 | 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | 1642 | */ |
| @@ -1569,10 +1646,11 @@ | ||
| 1569 | 1646 | void *p = NULL; |
| 1570 | 1647 | size_t n = 0; |
| 1571 | 1648 | size_t pageSize = 0; |
| 1572 | 1649 | size_t blobSize = 0; |
| 1573 | 1650 | |
| 1651 | + assert( !db_have_saved_encryption_key() ); | |
| 1574 | 1652 | blobSize = blob_size(pKey); |
| 1575 | 1653 | if( blobSize==0 ) return; |
| 1576 | 1654 | fossil_get_page_size(&pageSize); |
| 1577 | 1655 | assert( pageSize>0 ); |
| 1578 | 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | ||
| 1599 | 1677 | |
| 1600 | 1678 | /* |
| 1601 | 1679 | ** This function sets the saved database encryption key to the specified |
| 1602 | 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | 1681 | */ |
| 1604 | -void db_set_saved_encryption_key( | |
| 1682 | +static void db_set_saved_encryption_key( | |
| 1605 | 1683 | Blob *pKey |
| 1606 | 1684 | ){ |
| 1607 | 1685 | if( zSavedKey!=NULL ){ |
| 1608 | 1686 | size_t blobSize = blob_size(pKey); |
| 1609 | 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | ||
| 1619 | 1697 | }else{ |
| 1620 | 1698 | db_save_encryption_key(pKey); |
| 1621 | 1699 | } |
| 1622 | 1700 | } |
| 1623 | 1701 | |
| 1702 | +/* | |
| 1703 | +** WEBPAGE: setseekey | |
| 1704 | +** | |
| 1705 | +** Sets the sets the saved database encryption key to one that gets passed | |
| 1706 | +** via the "key" query string parameter. If the saved database encryption | |
| 1707 | +** key has already been set, does nothing. This web page does not produce | |
| 1708 | +** any output on success or failure. No permissions are required and none | |
| 1709 | +** are checked (partially due to lack of encrypted database access). | |
| 1710 | +** | |
| 1711 | +** Query parameters: | |
| 1712 | +** | |
| 1713 | +** key The string to set as the saved database encryption | |
| 1714 | +** key. | |
| 1715 | +*/ | |
| 1716 | +void db_set_see_key_page(void){ | |
| 1717 | + Blob key; | |
| 1718 | + const char *zKey; | |
| 1719 | + if( db_have_saved_encryption_key() ){ | |
| 1720 | + fossil_trace("SEE: encryption key was already set\n"); | |
| 1721 | + return; | |
| 1722 | + } | |
| 1723 | + zKey = P("key"); | |
| 1724 | + blob_init(&key, 0, 0); | |
| 1725 | + if( zKey!=0 ){ | |
| 1726 | + PID_T processId; | |
| 1727 | + blob_set(&key, zKey); | |
| 1728 | + db_set_saved_encryption_key(&key); | |
| 1729 | + processId = db_maybe_handle_saved_encryption_key_for_process( | |
| 1730 | + SEE_KEY_WRITE | |
| 1731 | + ); | |
| 1732 | + fossil_trace("SEE: set encryption key for process %lu, length %u\n", | |
| 1733 | + (unsigned long)processId, blob_size(&key)); | |
| 1734 | + }else{ | |
| 1735 | + fossil_trace("SEE: no encryption key specified\n"); | |
| 1736 | + } | |
| 1737 | + blob_reset(&key); | |
| 1738 | +} | |
| 1739 | + | |
| 1740 | +/* | |
| 1741 | +** WEBPAGE: unsetseekey | |
| 1742 | +** | |
| 1743 | +** Sets the saved database encryption key to zeros in the current and parent | |
| 1744 | +** Fossil processes. This web page does not produce any output on success | |
| 1745 | +** or failure. Setup permission is required. | |
| 1746 | +*/ | |
| 1747 | +void db_unset_see_key_page(void){ | |
| 1748 | + PID_T processId; | |
| 1749 | + login_check_credentials(); | |
| 1750 | + if( !g.perm.Setup ){ login_needed(0); return; } | |
| 1751 | + processId = db_maybe_handle_saved_encryption_key_for_process( | |
| 1752 | + SEE_KEY_ZERO | |
| 1753 | + ); | |
| 1754 | + fossil_trace("SEE: unset encryption key for process %lu\n", | |
| 1755 | + (unsigned long)processId); | |
| 1756 | +} | |
| 1757 | + | |
| 1758 | +/* | |
| 1759 | +** This function reads the saved database encryption key from the | |
| 1760 | +** specified Fossil parent process. This is only necessary (or | |
| 1761 | +** functional) on Windows or Linux. | |
| 1762 | +*/ | |
| 1763 | +static void db_read_saved_encryption_key_from_process( | |
| 1764 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1765 | + LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ | |
| 1766 | + SIZE_T nSize /* Size of saved key buffer in the parent process. */ | |
| 1767 | +){ | |
| 1768 | + void *p = NULL; | |
| 1769 | + size_t n = 0; | |
| 1770 | + size_t pageSize = 0; | |
| 1771 | + | |
| 1772 | + fossil_get_page_size(&pageSize); | |
| 1773 | + assert( pageSize>0 ); | |
| 1774 | + if( nSize>pageSize ){ | |
| 1775 | + fossil_panic("key too large: %u versus %u", nSize, pageSize); | |
| 1776 | + } | |
| 1777 | + p = fossil_secure_alloc_page(&n); | |
| 1778 | + assert( p!=NULL ); | |
| 1779 | + assert( n==pageSize ); | |
| 1780 | + assert( n>=nSize ); | |
| 1781 | + { | |
| 1782 | +#if defined(_WIN32) | |
| 1783 | + HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); | |
| 1784 | + if( hProcess!=NULL ){ | |
| 1785 | + SIZE_T nRead = 0; | |
| 1786 | + if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ | |
| 1787 | + CloseHandle(hProcess); | |
| 1788 | + if( nRead==nSize ){ | |
| 1789 | + db_unsave_encryption_key(); | |
| 1790 | + zSavedKey = p; | |
| 1791 | + savedKeySize = n; | |
| 1792 | + }else{ | |
| 1793 | + fossil_secure_free_page(p, n); | |
| 1794 | + fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", | |
| 1795 | + nRead, nSize, pAddress, processId); | |
| 1796 | + } | |
| 1797 | + }else{ | |
| 1798 | + CloseHandle(hProcess); | |
| 1799 | + fossil_secure_free_page(p, n); | |
| 1800 | + fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1801 | + pAddress, processId, GetLastError()); | |
| 1802 | + } | |
| 1803 | + }else{ | |
| 1804 | + fossil_secure_free_page(p, n); | |
| 1805 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1806 | + } | |
| 1807 | +#elif defined(__linux__) | |
| 1808 | + ssize_t nRead; | |
| 1809 | + struct iovec liov = {0}; | |
| 1810 | + struct iovec riov = {0}; | |
| 1811 | + liov.iov_base = p; | |
| 1812 | + liov.iov_len = n; | |
| 1813 | + riov.iov_base = pAddress; | |
| 1814 | + riov.iov_len = nSize; | |
| 1815 | + nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0); | |
| 1816 | + if( nRead==nSize ){ | |
| 1817 | + db_unsave_encryption_key(); | |
| 1818 | + zSavedKey = p; | |
| 1819 | + savedKeySize = n; | |
| 1820 | + }else{ | |
| 1821 | + fossil_secure_free_page(p, n); | |
| 1822 | + fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu", | |
| 1823 | + nRead, nSize, pAddress, (unsigned long)processId); | |
| 1824 | + } | |
| 1825 | +#else | |
| 1826 | + fossil_secure_free_page(p, n); | |
| 1827 | + fossil_trace("db_read_saved_encryption_key_from_process unsupported"); | |
| 1828 | +#endif | |
| 1829 | + } | |
| 1830 | +} | |
| 1831 | + | |
| 1832 | +/* | |
| 1833 | +** This function writes the saved database encryption key into the | |
| 1834 | +** specified Fossil parent process. This is only necessary (or | |
| 1835 | +** functional) on Windows or Linux. | |
| 1836 | +*/ | |
| 1837 | +static void db_write_saved_encryption_key_to_process( | |
| 1838 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1839 | + LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ | |
| 1840 | + SIZE_T nSize /* Size of saved key buffer in the parent process. */ | |
| 1841 | +){ | |
| 1842 | + void *p = db_get_saved_encryption_key(); | |
| 1843 | + size_t n = db_get_saved_encryption_key_size(); | |
| 1844 | + size_t pageSize = 0; | |
| 1845 | + | |
| 1846 | + fossil_get_page_size(&pageSize); | |
| 1847 | + assert( pageSize>0 ); | |
| 1848 | + if( nSize>pageSize ){ | |
| 1849 | + fossil_panic("key too large: %u versus %u", nSize, pageSize); | |
| 1850 | + } | |
| 1851 | + assert( p!=NULL ); | |
| 1852 | + assert( n==pageSize ); | |
| 1853 | + assert( n>=nSize ); | |
| 1854 | + { | |
| 1624 | 1855 | #if defined(_WIN32) |
| 1856 | + HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, | |
| 1857 | + FALSE, processId); | |
| 1858 | + if( hProcess!=NULL ){ | |
| 1859 | + SIZE_T nWrite = 0; | |
| 1860 | + if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ | |
| 1861 | + CloseHandle(hProcess); | |
| 1862 | + if( nWrite!=nSize ){ | |
| 1863 | + fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu", | |
| 1864 | + nWrite, nSize, pAddress, processId); | |
| 1865 | + } | |
| 1866 | + }else{ | |
| 1867 | + CloseHandle(hProcess); | |
| 1868 | + fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1869 | + pAddress, processId, GetLastError()); | |
| 1870 | + } | |
| 1871 | + }else{ | |
| 1872 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1873 | + } | |
| 1874 | +#elif defined(__linux__) | |
| 1875 | + ssize_t nWrite; | |
| 1876 | + struct iovec liov = {0}; | |
| 1877 | + struct iovec riov = {0}; | |
| 1878 | + liov.iov_base = p; | |
| 1879 | + liov.iov_len = n; | |
| 1880 | + riov.iov_base = pAddress; | |
| 1881 | + riov.iov_len = nSize; | |
| 1882 | + nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); | |
| 1883 | + if( nWrite!=nSize ){ | |
| 1884 | + fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu", | |
| 1885 | + nWrite, nSize, pAddress, (unsigned long)processId); | |
| 1886 | + } | |
| 1887 | +#else | |
| 1888 | + fossil_trace("db_write_saved_encryption_key_to_process unsupported"); | |
| 1889 | +#endif | |
| 1890 | + } | |
| 1891 | +} | |
| 1892 | + | |
| 1625 | 1893 | /* |
| 1626 | -** This function sets the saved database encryption key to one that gets | |
| 1627 | -** read from the specified Fossil parent process. This is only necessary | |
| 1628 | -** (or functional) on Windows. | |
| 1894 | +** This function zeros the saved database encryption key in the specified | |
| 1895 | +** Fossil parent process. This is only necessary (or functional) on | |
| 1896 | +** Windows or Linux. | |
| 1629 | 1897 | */ |
| 1630 | -void db_read_saved_encryption_key_from_process( | |
| 1631 | - DWORD processId, /* Identifier for Fossil parent process. */ | |
| 1898 | +static void db_zero_saved_encryption_key_in_process( | |
| 1899 | + PID_T processId, /* Identifier for Fossil parent process. */ | |
| 1632 | 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | 1902 | ){ |
| 1635 | 1903 | void *p = NULL; |
| 1636 | 1904 | size_t n = 0; |
| 1637 | 1905 | size_t pageSize = 0; |
| 1638 | - HANDLE hProcess = NULL; | |
| 1639 | 1906 | |
| 1640 | 1907 | fossil_get_page_size(&pageSize); |
| 1641 | 1908 | assert( pageSize>0 ); |
| 1642 | 1909 | if( nSize>pageSize ){ |
| 1643 | 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | ||
| 1644 | 1911 | } |
| 1645 | 1912 | p = fossil_secure_alloc_page(&n); |
| 1646 | 1913 | assert( p!=NULL ); |
| 1647 | 1914 | assert( n==pageSize ); |
| 1648 | 1915 | assert( n>=nSize ); |
| 1649 | - hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); | |
| 1650 | - if( hProcess!=NULL ){ | |
| 1651 | - SIZE_T nRead = 0; | |
| 1652 | - if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ | |
| 1653 | - CloseHandle(hProcess); | |
| 1654 | - if( nRead==nSize ){ | |
| 1655 | - db_unsave_encryption_key(); | |
| 1656 | - zSavedKey = p; | |
| 1657 | - savedKeySize = n; | |
| 1658 | - }else{ | |
| 1659 | - fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", | |
| 1660 | - nRead, nSize, pAddress, processId); | |
| 1661 | - } | |
| 1662 | - }else{ | |
| 1663 | - CloseHandle(hProcess); | |
| 1664 | - fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1665 | - pAddress, processId, GetLastError()); | |
| 1666 | - } | |
| 1667 | - }else{ | |
| 1668 | - fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1916 | + { | |
| 1917 | +#if defined(_WIN32) | |
| 1918 | + HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, | |
| 1919 | + FALSE, processId); | |
| 1920 | + if( hProcess!=NULL ){ | |
| 1921 | + SIZE_T nWrite = 0; | |
| 1922 | + if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ | |
| 1923 | + CloseHandle(hProcess); | |
| 1924 | + fossil_secure_free_page(p, n); | |
| 1925 | + if( nWrite!=nSize ){ | |
| 1926 | + fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu", | |
| 1927 | + nWrite, nSize, pAddress, processId); | |
| 1928 | + } | |
| 1929 | + }else{ | |
| 1930 | + CloseHandle(hProcess); | |
| 1931 | + fossil_secure_free_page(p, n); | |
| 1932 | + fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize, | |
| 1933 | + pAddress, processId, GetLastError()); | |
| 1934 | + } | |
| 1935 | + }else{ | |
| 1936 | + fossil_secure_free_page(p, n); | |
| 1937 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1938 | + } | |
| 1939 | +#elif defined(__linux__) | |
| 1940 | + ssize_t nWrite; | |
| 1941 | + struct iovec liov = {0}; | |
| 1942 | + struct iovec riov = {0}; | |
| 1943 | + liov.iov_base = p; | |
| 1944 | + liov.iov_len = n; | |
| 1945 | + riov.iov_base = pAddress; | |
| 1946 | + riov.iov_len = nSize; | |
| 1947 | + nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); | |
| 1948 | + if( nWrite!=nSize ){ | |
| 1949 | + fossil_secure_free_page(p, n); | |
| 1950 | + fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu", | |
| 1951 | + nWrite, nSize, pAddress, (unsigned long)processId); | |
| 1952 | + } | |
| 1953 | +#else | |
| 1954 | + fossil_secure_free_page(p, n); | |
| 1955 | + fossil_trace("db_zero_saved_encryption_key_in_process unsupported"); | |
| 1956 | +#endif | |
| 1669 | 1957 | } |
| 1670 | 1958 | } |
| 1671 | 1959 | |
| 1672 | 1960 | /* |
| 1673 | 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | 1963 | ** address, and size (in bytes) of the database encryption key. This is |
| 1676 | -** only necessary (or functional) on Windows. | |
| 1964 | +** only necessary (or functional) on Windows or Linux. | |
| 1677 | 1965 | */ |
| 1678 | -void db_read_saved_encryption_key_from_process_via_th1( | |
| 1679 | - const char *zConfig /* The TH1 script to evaluate. */ | |
| 1966 | +static PID_T db_handle_saved_encryption_key_for_process_via_th1( | |
| 1967 | + const char *zConfig, /* The TH1 script to evaluate. */ | |
| 1968 | + int eType /* Non-zero to write key to parent process -OR- | |
| 1969 | + * zero to read it from the parent process. */ | |
| 1680 | 1970 | ){ |
| 1971 | + PID_T processId = 0; | |
| 1681 | 1972 | int rc; |
| 1682 | 1973 | char *zResult; |
| 1683 | 1974 | char *zPwd = file_getcwd(0, 0); |
| 1684 | 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | ||
| 1686 | 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | 1978 | if( rc!=TH_OK ){ |
| 1688 | 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | 1980 | } |
| 1690 | 1981 | if( zResult ){ |
| 1691 | - DWORD processId = 0; | |
| 1692 | 1982 | LPVOID pAddress = NULL; |
| 1693 | 1983 | SIZE_T nSize = 0; |
| 1694 | 1984 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1695 | - db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 1985 | + if( eType==SEE_KEY_READ ){ | |
| 1986 | + db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 1987 | + }else if( eType==SEE_KEY_WRITE ){ | |
| 1988 | + db_write_saved_encryption_key_to_process(processId, pAddress, nSize); | |
| 1989 | + }else if( eType==SEE_KEY_ZERO ){ | |
| 1990 | + db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); | |
| 1991 | + }else{ | |
| 1992 | + fossil_panic("unsupported SEE key operation %d", eType); | |
| 1993 | + } | |
| 1696 | 1994 | } |
| 1697 | 1995 | file_chdir(zPwd, 0); |
| 1698 | 1996 | fossil_free(zPwd); |
| 1997 | + return processId; | |
| 1699 | 1998 | } |
| 1700 | -#endif /* defined(_WIN32) */ | |
| 1999 | + | |
| 2000 | +/* | |
| 2001 | +** This function sets the saved database encryption key to one that gets | |
| 2002 | +** read from the specified Fossil parent process, if applicable. This is | |
| 2003 | +** only necessary (or functional) on Windows or Linux. | |
| 2004 | +*/ | |
| 2005 | +PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){ | |
| 2006 | + PID_T processId = 0; | |
| 2007 | + g.zPidKey = find_option("usepidkey",0,1); | |
| 2008 | + if( !g.zPidKey ){ | |
| 2009 | + g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY"); | |
| 2010 | + } | |
| 2011 | + if( g.zPidKey ){ | |
| 2012 | + LPVOID pAddress = NULL; | |
| 2013 | + SIZE_T nSize = 0; | |
| 2014 | + parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); | |
| 2015 | + if( eType==SEE_KEY_READ ){ | |
| 2016 | + db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 2017 | + }else if( eType==SEE_KEY_WRITE ){ | |
| 2018 | + db_write_saved_encryption_key_to_process(processId, pAddress, nSize); | |
| 2019 | + }else if( eType==SEE_KEY_ZERO ){ | |
| 2020 | + db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); | |
| 2021 | + }else{ | |
| 2022 | + fossil_panic("unsupported SEE key operation %d", eType); | |
| 2023 | + } | |
| 2024 | + }else{ | |
| 2025 | + const char *zSeeDbConfig = find_option("seedbcfg",0,1); | |
| 2026 | + if( !zSeeDbConfig ){ | |
| 2027 | + zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); | |
| 2028 | + } | |
| 2029 | + if( zSeeDbConfig ){ | |
| 2030 | + processId = db_handle_saved_encryption_key_for_process_via_th1( | |
| 2031 | + zSeeDbConfig, eType | |
| 2032 | + ); | |
| 2033 | + } | |
| 2034 | + } | |
| 2035 | + return processId; | |
| 2036 | +} | |
| 1701 | 2037 | #endif /* USE_SEE */ |
| 1702 | 2038 | |
| 1703 | 2039 | /* |
| 1704 | 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | 2041 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | ||
| 3294 | 3630 | noWarn = 1; |
| 3295 | 3631 | } |
| 3296 | 3632 | } |
| 3297 | 3633 | blob_reset(&versionedPathname); |
| 3298 | 3634 | if( found ){ |
| 3635 | + blob_strip_comment_lines(&setting, &setting); | |
| 3299 | 3636 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3300 | 3637 | ** on boolean properties */ |
| 3301 | 3638 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3302 | 3639 | } |
| 3303 | 3640 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | ||
| 4158 | 4495 | ** every time the backoffice runs. This can be used for debugging, |
| 4159 | 4496 | ** to ensure that backoffice is running appropriately. |
| 4160 | 4497 | */ |
| 4161 | 4498 | /* |
| 4162 | 4499 | ** SETTING: binary-glob width=40 versionable block-text |
| 4163 | -** The VALUE of this setting is a comma or newline-separated list of | |
| 4164 | -** GLOB patterns that should be treated as binary files | |
| 4165 | -** for committing and merging purposes. Example: *.jpg | |
| 4500 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4501 | +** that should be treated as "binary" for committing and merging | |
| 4502 | +** purposes. Example: *.jpg,*.png The parsing rules are complex; | |
| 4503 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4166 | 4504 | */ |
| 4167 | 4505 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4168 | 4506 | /* |
| 4169 | 4507 | ** SETTING: case-sensitive boolean default=off |
| 4170 | 4508 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | ||
| 4182 | 4520 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4183 | 4521 | */ |
| 4184 | 4522 | #endif |
| 4185 | 4523 | /* |
| 4186 | 4524 | ** SETTING: clean-glob width=40 versionable block-text |
| 4187 | -** The VALUE of this setting is a comma or newline-separated list of GLOB | |
| 4188 | -** patterns specifying files that the "clean" command will | |
| 4189 | -** delete without prompting or allowing undo. | |
| 4190 | -** Example: *.a,*.lib,*.o | |
| 4525 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4526 | +** that the "clean" command will delete without prompting or allowing | |
| 4527 | +** undo. Example: *.a,*.o,*.so The parsing rules are complex; | |
| 4528 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4191 | 4529 | */ |
| 4192 | 4530 | /* |
| 4193 | 4531 | ** SETTING: clearsign boolean default=off |
| 4194 | 4532 | ** When enabled, fossil will attempt to sign all commits |
| 4195 | 4533 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | ||
| 4217 | 4555 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4218 | 4556 | ** configured through the /setup_timeline web page. |
| 4219 | 4557 | */ |
| 4220 | 4558 | /* |
| 4221 | 4559 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4222 | -** The value is a comma or newline-separated list of GLOB patterns for | |
| 4223 | -** text files in which it is ok to have CR, CR+LF or mixed | |
| 4224 | -** line endings. Set to "*" to disable CR+LF checking. | |
| 4560 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4561 | +** in which it is allowed to have CR, CR+LF or mixed line endings, | |
| 4562 | +** suppressing Fossil's normal warning about this. Set it to "*" to | |
| 4563 | +** disable CR+LF checking entirely. Example: *.md,*.txt | |
| 4225 | 4564 | ** The crnl-glob setting is a compatibility alias. |
| 4226 | 4565 | */ |
| 4227 | 4566 | /* |
| 4228 | 4567 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4229 | 4568 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | ||
| 4265 | 4604 | ** The value is an external command that will launch the |
| 4266 | 4605 | ** text editor command used for check-in comments. |
| 4267 | 4606 | */ |
| 4268 | 4607 | /* |
| 4269 | 4608 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4270 | -** The value is a comma or newline-separated list of pathnames. On | |
| 4271 | -** update and checkout commands, if no file or directory | |
| 4272 | -** exists with that name, an empty directory will be | |
| 4273 | -** created. | |
| 4609 | +** The value is a list of pathnames parsed according to the same rules as | |
| 4610 | +** the *-glob settings. On update and checkout commands, if no directory | |
| 4611 | +** exists with that name, an empty directory will be be created, even if | |
| 4612 | +** it must create one or more parent directories. | |
| 4274 | 4613 | */ |
| 4275 | 4614 | /* |
| 4276 | 4615 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4277 | -** The value is a comma or newline-separated list of GLOB | |
| 4278 | -** patterns specifying files that the "commit" command will | |
| 4279 | -** ignore when issuing warnings about text files that may | |
| 4280 | -** use another encoding than ASCII or UTF-8. Set to "*" | |
| 4281 | -** to disable encoding checking. | |
| 4616 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4617 | +** the "commit" command will ignore when issuing warnings about text files | |
| 4618 | +** that may use another encoding than ASCII or UTF-8. Set to "*" to disable | |
| 4619 | +** encoding checking. Example: *.md,*.txt The parsing rules are complex; | |
| 4620 | +** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4282 | 4621 | */ |
| 4283 | 4622 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4284 | 4623 | /* |
| 4285 | 4624 | ** SETTING: exec-rel-paths boolean default=on |
| 4286 | 4625 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | ||
| 4295 | 4634 | */ |
| 4296 | 4635 | #endif |
| 4297 | 4636 | |
| 4298 | 4637 | /* |
| 4299 | 4638 | ** SETTING: fileedit-glob width=40 block-text |
| 4300 | -** A comma- or newline-separated list of globs of filenames | |
| 4301 | -** which are allowed to be edited using the /fileedit page. | |
| 4302 | -** An empty list prohibits editing via that page. Note that | |
| 4303 | -** it cannot edit binary files, so the list should not | |
| 4639 | +** The VALUE of this setting is a list of GLOB patterns matching files | |
| 4640 | +** which are allowed to be edited using the /fileedit page. An empty list | |
| 4641 | +** suppresses the feature. Example: *.md,*.txt The parsing rules are | |
| 4642 | +** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4643 | +** Note that /fileedit cannot edit binary files, so the list should not | |
| 4304 | 4644 | ** contain any globs for, e.g., images or PDFs. |
| 4305 | 4645 | */ |
| 4306 | 4646 | /* |
| 4307 | 4647 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4308 | 4648 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | ||
| 4340 | 4680 | ** If true, then the Fossil web server will redirect unencrypted |
| 4341 | 4681 | ** login screen requests to HTTPS. |
| 4342 | 4682 | */ |
| 4343 | 4683 | /* |
| 4344 | 4684 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4345 | -** The value is a list of GLOB patterns, separated by spaces, | |
| 4346 | -** commas, or newlines, specifying files that the "add", | |
| 4347 | -** "addremove", "clean", and "extras" commands will ignore. | |
| 4348 | -** | |
| 4349 | -** Example: *.log, customCode.c, notes.txt | |
| 4685 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4686 | +** the "add", "addremove", "clean", and "extras" commands will ignore. | |
| 4687 | +** Example: *.log,notes.txt The parsing rules are complex; see | |
| 4688 | +** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4350 | 4689 | */ |
| 4351 | 4690 | /* |
| 4352 | 4691 | ** SETTING: keep-glob width=40 versionable block-text |
| 4353 | -** The value is list of GLOB patterns, separated by spaces, | |
| 4354 | -** commas, or newlines, specifying files that the "clean" | |
| 4355 | -** command will keep. | |
| 4692 | +** The VALUE of this setting is a list of GLOB patterns matching files that | |
| 4693 | +** the "clean" command must not delete. Example: build/precious.exe | |
| 4694 | +** The parsing rules are complex; see | |
| 4695 | +** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax | |
| 4356 | 4696 | */ |
| 4357 | 4697 | /* |
| 4358 | 4698 | ** SETTING: localauth boolean default=off |
| 4359 | 4699 | ** If enabled, require that HTTP connections from the loopback |
| 4360 | 4700 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4361 | 4701 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -31,25 +31,57 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #if defined(_WIN32) |
| 34 | # if USE_SEE |
| 35 | # include <windows.h> |
| 36 | # endif |
| 37 | #else |
| 38 | # include <pwd.h> |
| 39 | #endif |
| 40 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | # define SQLITE_HAS_CODEC |
| 42 | #endif |
| 43 | #include <sqlite3.h> |
| 44 | #include <sys/types.h> |
| 45 | #include <sys/stat.h> |
| 46 | #include <unistd.h> |
| 47 | #include <time.h> |
| 48 | #include "db.h" |
| 49 | |
| 50 | #if INTERFACE |
| 51 | /* |
| 52 | ** An single SQL statement is represented as an instance of the following |
| 53 | ** structure. |
| 54 | */ |
| 55 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | |
| 1539 | static char *zSavedKey = 0; |
| 1540 | |
| 1541 | /* |
| 1542 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | */ |
| 1544 | size_t savedKeySize = 0; |
| 1545 | |
| 1546 | /* |
| 1547 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | ** no database encryption key is saved. |
| 1549 | */ |
| @@ -1556,10 +1607,36 @@ | |
| 1556 | ** -OR- zero if no database encryption key is saved. |
| 1557 | */ |
| 1558 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | return savedKeySize; |
| 1560 | } |
| 1561 | |
| 1562 | /* |
| 1563 | ** This function arranges for the database encryption key to be securely |
| 1564 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | */ |
| @@ -1569,10 +1646,11 @@ | |
| 1569 | void *p = NULL; |
| 1570 | size_t n = 0; |
| 1571 | size_t pageSize = 0; |
| 1572 | size_t blobSize = 0; |
| 1573 | |
| 1574 | blobSize = blob_size(pKey); |
| 1575 | if( blobSize==0 ) return; |
| 1576 | fossil_get_page_size(&pageSize); |
| 1577 | assert( pageSize>0 ); |
| 1578 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | |
| 1599 | |
| 1600 | /* |
| 1601 | ** This function sets the saved database encryption key to the specified |
| 1602 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | */ |
| 1604 | void db_set_saved_encryption_key( |
| 1605 | Blob *pKey |
| 1606 | ){ |
| 1607 | if( zSavedKey!=NULL ){ |
| 1608 | size_t blobSize = blob_size(pKey); |
| 1609 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | |
| 1619 | }else{ |
| 1620 | db_save_encryption_key(pKey); |
| 1621 | } |
| 1622 | } |
| 1623 | |
| 1624 | #if defined(_WIN32) |
| 1625 | /* |
| 1626 | ** This function sets the saved database encryption key to one that gets |
| 1627 | ** read from the specified Fossil parent process. This is only necessary |
| 1628 | ** (or functional) on Windows. |
| 1629 | */ |
| 1630 | void db_read_saved_encryption_key_from_process( |
| 1631 | DWORD processId, /* Identifier for Fossil parent process. */ |
| 1632 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | ){ |
| 1635 | void *p = NULL; |
| 1636 | size_t n = 0; |
| 1637 | size_t pageSize = 0; |
| 1638 | HANDLE hProcess = NULL; |
| 1639 | |
| 1640 | fossil_get_page_size(&pageSize); |
| 1641 | assert( pageSize>0 ); |
| 1642 | if( nSize>pageSize ){ |
| 1643 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | |
| 1644 | } |
| 1645 | p = fossil_secure_alloc_page(&n); |
| 1646 | assert( p!=NULL ); |
| 1647 | assert( n==pageSize ); |
| 1648 | assert( n>=nSize ); |
| 1649 | hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); |
| 1650 | if( hProcess!=NULL ){ |
| 1651 | SIZE_T nRead = 0; |
| 1652 | if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ |
| 1653 | CloseHandle(hProcess); |
| 1654 | if( nRead==nSize ){ |
| 1655 | db_unsave_encryption_key(); |
| 1656 | zSavedKey = p; |
| 1657 | savedKeySize = n; |
| 1658 | }else{ |
| 1659 | fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", |
| 1660 | nRead, nSize, pAddress, processId); |
| 1661 | } |
| 1662 | }else{ |
| 1663 | CloseHandle(hProcess); |
| 1664 | fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, |
| 1665 | pAddress, processId, GetLastError()); |
| 1666 | } |
| 1667 | }else{ |
| 1668 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1669 | } |
| 1670 | } |
| 1671 | |
| 1672 | /* |
| 1673 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | ** address, and size (in bytes) of the database encryption key. This is |
| 1676 | ** only necessary (or functional) on Windows. |
| 1677 | */ |
| 1678 | void db_read_saved_encryption_key_from_process_via_th1( |
| 1679 | const char *zConfig /* The TH1 script to evaluate. */ |
| 1680 | ){ |
| 1681 | int rc; |
| 1682 | char *zResult; |
| 1683 | char *zPwd = file_getcwd(0, 0); |
| 1684 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | |
| 1686 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | if( rc!=TH_OK ){ |
| 1688 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | } |
| 1690 | if( zResult ){ |
| 1691 | DWORD processId = 0; |
| 1692 | LPVOID pAddress = NULL; |
| 1693 | SIZE_T nSize = 0; |
| 1694 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1695 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 1696 | } |
| 1697 | file_chdir(zPwd, 0); |
| 1698 | fossil_free(zPwd); |
| 1699 | } |
| 1700 | #endif /* defined(_WIN32) */ |
| 1701 | #endif /* USE_SEE */ |
| 1702 | |
| 1703 | /* |
| 1704 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | |
| 3294 | noWarn = 1; |
| 3295 | } |
| 3296 | } |
| 3297 | blob_reset(&versionedPathname); |
| 3298 | if( found ){ |
| 3299 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3300 | ** on boolean properties */ |
| 3301 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3302 | } |
| 3303 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | |
| 4158 | ** every time the backoffice runs. This can be used for debugging, |
| 4159 | ** to ensure that backoffice is running appropriately. |
| 4160 | */ |
| 4161 | /* |
| 4162 | ** SETTING: binary-glob width=40 versionable block-text |
| 4163 | ** The VALUE of this setting is a comma or newline-separated list of |
| 4164 | ** GLOB patterns that should be treated as binary files |
| 4165 | ** for committing and merging purposes. Example: *.jpg |
| 4166 | */ |
| 4167 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4168 | /* |
| 4169 | ** SETTING: case-sensitive boolean default=off |
| 4170 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | |
| 4182 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4183 | */ |
| 4184 | #endif |
| 4185 | /* |
| 4186 | ** SETTING: clean-glob width=40 versionable block-text |
| 4187 | ** The VALUE of this setting is a comma or newline-separated list of GLOB |
| 4188 | ** patterns specifying files that the "clean" command will |
| 4189 | ** delete without prompting or allowing undo. |
| 4190 | ** Example: *.a,*.lib,*.o |
| 4191 | */ |
| 4192 | /* |
| 4193 | ** SETTING: clearsign boolean default=off |
| 4194 | ** When enabled, fossil will attempt to sign all commits |
| 4195 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | |
| 4217 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4218 | ** configured through the /setup_timeline web page. |
| 4219 | */ |
| 4220 | /* |
| 4221 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4222 | ** The value is a comma or newline-separated list of GLOB patterns for |
| 4223 | ** text files in which it is ok to have CR, CR+LF or mixed |
| 4224 | ** line endings. Set to "*" to disable CR+LF checking. |
| 4225 | ** The crnl-glob setting is a compatibility alias. |
| 4226 | */ |
| 4227 | /* |
| 4228 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4229 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | |
| 4265 | ** The value is an external command that will launch the |
| 4266 | ** text editor command used for check-in comments. |
| 4267 | */ |
| 4268 | /* |
| 4269 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4270 | ** The value is a comma or newline-separated list of pathnames. On |
| 4271 | ** update and checkout commands, if no file or directory |
| 4272 | ** exists with that name, an empty directory will be |
| 4273 | ** created. |
| 4274 | */ |
| 4275 | /* |
| 4276 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4277 | ** The value is a comma or newline-separated list of GLOB |
| 4278 | ** patterns specifying files that the "commit" command will |
| 4279 | ** ignore when issuing warnings about text files that may |
| 4280 | ** use another encoding than ASCII or UTF-8. Set to "*" |
| 4281 | ** to disable encoding checking. |
| 4282 | */ |
| 4283 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4284 | /* |
| 4285 | ** SETTING: exec-rel-paths boolean default=on |
| 4286 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | |
| 4295 | */ |
| 4296 | #endif |
| 4297 | |
| 4298 | /* |
| 4299 | ** SETTING: fileedit-glob width=40 block-text |
| 4300 | ** A comma- or newline-separated list of globs of filenames |
| 4301 | ** which are allowed to be edited using the /fileedit page. |
| 4302 | ** An empty list prohibits editing via that page. Note that |
| 4303 | ** it cannot edit binary files, so the list should not |
| 4304 | ** contain any globs for, e.g., images or PDFs. |
| 4305 | */ |
| 4306 | /* |
| 4307 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4308 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | |
| 4340 | ** If true, then the Fossil web server will redirect unencrypted |
| 4341 | ** login screen requests to HTTPS. |
| 4342 | */ |
| 4343 | /* |
| 4344 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4345 | ** The value is a list of GLOB patterns, separated by spaces, |
| 4346 | ** commas, or newlines, specifying files that the "add", |
| 4347 | ** "addremove", "clean", and "extras" commands will ignore. |
| 4348 | ** |
| 4349 | ** Example: *.log, customCode.c, notes.txt |
| 4350 | */ |
| 4351 | /* |
| 4352 | ** SETTING: keep-glob width=40 versionable block-text |
| 4353 | ** The value is list of GLOB patterns, separated by spaces, |
| 4354 | ** commas, or newlines, specifying files that the "clean" |
| 4355 | ** command will keep. |
| 4356 | */ |
| 4357 | /* |
| 4358 | ** SETTING: localauth boolean default=off |
| 4359 | ** If enabled, require that HTTP connections from the loopback |
| 4360 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4361 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -31,25 +31,57 @@ | |
| 31 | */ |
| 32 | #include "config.h" |
| 33 | #if defined(_WIN32) |
| 34 | # if USE_SEE |
| 35 | # include <windows.h> |
| 36 | # define GETPID (int)GetCurrentProcessId |
| 37 | # endif |
| 38 | #else |
| 39 | # include <pwd.h> |
| 40 | # if USE_SEE |
| 41 | # define GETPID getpid |
| 42 | # endif |
| 43 | #endif |
| 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 45 | # define SQLITE_HAS_CODEC |
| 46 | #endif |
| 47 | #if USE_SEE && defined(__linux__) |
| 48 | # include <sys/uio.h> |
| 49 | #endif |
| 50 | #include <sqlite3.h> |
| 51 | #include <sys/types.h> |
| 52 | #include <sys/stat.h> |
| 53 | #include <unistd.h> |
| 54 | #include <time.h> |
| 55 | |
| 56 | /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ |
| 57 | #if USE_SEE |
| 58 | #if defined(_WIN32) |
| 59 | typedef DWORD PID_T; |
| 60 | #else |
| 61 | typedef pid_t PID_T; |
| 62 | #endif |
| 63 | #endif |
| 64 | |
| 65 | #include "db.h" |
| 66 | |
| 67 | #if INTERFACE |
| 68 | /* |
| 69 | ** Type definitions used for handling the saved encryption key for SEE. |
| 70 | */ |
| 71 | #if !defined(_WIN32) |
| 72 | typedef void *LPVOID; |
| 73 | typedef size_t SIZE_T; |
| 74 | #endif |
| 75 | |
| 76 | /* |
| 77 | ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al. |
| 78 | */ |
| 79 | #define SEE_KEY_READ ((int)0) |
| 80 | #define SEE_KEY_WRITE ((int)1) |
| 81 | #define SEE_KEY_ZERO ((int)2) |
| 82 | |
| 83 | /* |
| 84 | ** An single SQL statement is represented as an instance of the following |
| 85 | ** structure. |
| 86 | */ |
| 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | |
| 1571 | static char *zSavedKey = 0; |
| 1572 | |
| 1573 | /* |
| 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1575 | */ |
| 1576 | static size_t savedKeySize = 0; |
| 1577 | |
| 1578 | /* |
| 1579 | ** This function returns non-zero if there is a saved database encryption |
| 1580 | ** key available. |
| 1581 | */ |
| 1582 | int db_have_saved_encryption_key(){ |
| 1583 | return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize); |
| 1584 | } |
| 1585 | |
| 1586 | /* |
| 1587 | ** This function returns non-zero if the specified database encryption key |
| 1588 | ** is valid. |
| 1589 | */ |
| 1590 | int db_is_valid_saved_encryption_key(const char *p, size_t n){ |
| 1591 | if( p==0 ) return 0; |
| 1592 | if( n==0 ) return 0; |
| 1593 | if( p[0]==0 ) return 0; |
| 1594 | return 1; |
| 1595 | } |
| 1596 | |
| 1597 | /* |
| 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1599 | ** no database encryption key is saved. |
| 1600 | */ |
| @@ -1556,10 +1607,36 @@ | |
| 1607 | ** -OR- zero if no database encryption key is saved. |
| 1608 | */ |
| 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1610 | return savedKeySize; |
| 1611 | } |
| 1612 | |
| 1613 | /* |
| 1614 | ** This function arranges for the saved database encryption key buffer |
| 1615 | ** to be allocated and then sets up the environment variable to allow |
| 1616 | ** a child process to initialize it with the actual database encryption |
| 1617 | ** key. |
| 1618 | */ |
| 1619 | void db_setup_for_saved_encryption_key(){ |
| 1620 | void *p = NULL; |
| 1621 | size_t n = 0; |
| 1622 | size_t pageSize = 0; |
| 1623 | Blob pidKey; |
| 1624 | |
| 1625 | assert( !db_have_saved_encryption_key() ); |
| 1626 | db_unsave_encryption_key(); |
| 1627 | fossil_get_page_size(&pageSize); |
| 1628 | assert( pageSize>0 ); |
| 1629 | p = fossil_secure_alloc_page(&n); |
| 1630 | assert( p!=NULL ); |
| 1631 | assert( n==pageSize ); |
| 1632 | blob_zero(&pidKey); |
| 1633 | blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n); |
| 1634 | fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey)); |
| 1635 | zSavedKey = p; |
| 1636 | savedKeySize = n; |
| 1637 | } |
| 1638 | |
| 1639 | /* |
| 1640 | ** This function arranges for the database encryption key to be securely |
| 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1642 | */ |
| @@ -1569,10 +1646,11 @@ | |
| 1646 | void *p = NULL; |
| 1647 | size_t n = 0; |
| 1648 | size_t pageSize = 0; |
| 1649 | size_t blobSize = 0; |
| 1650 | |
| 1651 | assert( !db_have_saved_encryption_key() ); |
| 1652 | blobSize = blob_size(pKey); |
| 1653 | if( blobSize==0 ) return; |
| 1654 | fossil_get_page_size(&pageSize); |
| 1655 | assert( pageSize>0 ); |
| 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | |
| 1677 | |
| 1678 | /* |
| 1679 | ** This function sets the saved database encryption key to the specified |
| 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1681 | */ |
| 1682 | static void db_set_saved_encryption_key( |
| 1683 | Blob *pKey |
| 1684 | ){ |
| 1685 | if( zSavedKey!=NULL ){ |
| 1686 | size_t blobSize = blob_size(pKey); |
| 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | |
| 1697 | }else{ |
| 1698 | db_save_encryption_key(pKey); |
| 1699 | } |
| 1700 | } |
| 1701 | |
| 1702 | /* |
| 1703 | ** WEBPAGE: setseekey |
| 1704 | ** |
| 1705 | ** Sets the sets the saved database encryption key to one that gets passed |
| 1706 | ** via the "key" query string parameter. If the saved database encryption |
| 1707 | ** key has already been set, does nothing. This web page does not produce |
| 1708 | ** any output on success or failure. No permissions are required and none |
| 1709 | ** are checked (partially due to lack of encrypted database access). |
| 1710 | ** |
| 1711 | ** Query parameters: |
| 1712 | ** |
| 1713 | ** key The string to set as the saved database encryption |
| 1714 | ** key. |
| 1715 | */ |
| 1716 | void db_set_see_key_page(void){ |
| 1717 | Blob key; |
| 1718 | const char *zKey; |
| 1719 | if( db_have_saved_encryption_key() ){ |
| 1720 | fossil_trace("SEE: encryption key was already set\n"); |
| 1721 | return; |
| 1722 | } |
| 1723 | zKey = P("key"); |
| 1724 | blob_init(&key, 0, 0); |
| 1725 | if( zKey!=0 ){ |
| 1726 | PID_T processId; |
| 1727 | blob_set(&key, zKey); |
| 1728 | db_set_saved_encryption_key(&key); |
| 1729 | processId = db_maybe_handle_saved_encryption_key_for_process( |
| 1730 | SEE_KEY_WRITE |
| 1731 | ); |
| 1732 | fossil_trace("SEE: set encryption key for process %lu, length %u\n", |
| 1733 | (unsigned long)processId, blob_size(&key)); |
| 1734 | }else{ |
| 1735 | fossil_trace("SEE: no encryption key specified\n"); |
| 1736 | } |
| 1737 | blob_reset(&key); |
| 1738 | } |
| 1739 | |
| 1740 | /* |
| 1741 | ** WEBPAGE: unsetseekey |
| 1742 | ** |
| 1743 | ** Sets the saved database encryption key to zeros in the current and parent |
| 1744 | ** Fossil processes. This web page does not produce any output on success |
| 1745 | ** or failure. Setup permission is required. |
| 1746 | */ |
| 1747 | void db_unset_see_key_page(void){ |
| 1748 | PID_T processId; |
| 1749 | login_check_credentials(); |
| 1750 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 1751 | processId = db_maybe_handle_saved_encryption_key_for_process( |
| 1752 | SEE_KEY_ZERO |
| 1753 | ); |
| 1754 | fossil_trace("SEE: unset encryption key for process %lu\n", |
| 1755 | (unsigned long)processId); |
| 1756 | } |
| 1757 | |
| 1758 | /* |
| 1759 | ** This function reads the saved database encryption key from the |
| 1760 | ** specified Fossil parent process. This is only necessary (or |
| 1761 | ** functional) on Windows or Linux. |
| 1762 | */ |
| 1763 | static void db_read_saved_encryption_key_from_process( |
| 1764 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1765 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1766 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1767 | ){ |
| 1768 | void *p = NULL; |
| 1769 | size_t n = 0; |
| 1770 | size_t pageSize = 0; |
| 1771 | |
| 1772 | fossil_get_page_size(&pageSize); |
| 1773 | assert( pageSize>0 ); |
| 1774 | if( nSize>pageSize ){ |
| 1775 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| 1776 | } |
| 1777 | p = fossil_secure_alloc_page(&n); |
| 1778 | assert( p!=NULL ); |
| 1779 | assert( n==pageSize ); |
| 1780 | assert( n>=nSize ); |
| 1781 | { |
| 1782 | #if defined(_WIN32) |
| 1783 | HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId); |
| 1784 | if( hProcess!=NULL ){ |
| 1785 | SIZE_T nRead = 0; |
| 1786 | if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){ |
| 1787 | CloseHandle(hProcess); |
| 1788 | if( nRead==nSize ){ |
| 1789 | db_unsave_encryption_key(); |
| 1790 | zSavedKey = p; |
| 1791 | savedKeySize = n; |
| 1792 | }else{ |
| 1793 | fossil_secure_free_page(p, n); |
| 1794 | fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu", |
| 1795 | nRead, nSize, pAddress, processId); |
| 1796 | } |
| 1797 | }else{ |
| 1798 | CloseHandle(hProcess); |
| 1799 | fossil_secure_free_page(p, n); |
| 1800 | fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize, |
| 1801 | pAddress, processId, GetLastError()); |
| 1802 | } |
| 1803 | }else{ |
| 1804 | fossil_secure_free_page(p, n); |
| 1805 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1806 | } |
| 1807 | #elif defined(__linux__) |
| 1808 | ssize_t nRead; |
| 1809 | struct iovec liov = {0}; |
| 1810 | struct iovec riov = {0}; |
| 1811 | liov.iov_base = p; |
| 1812 | liov.iov_len = n; |
| 1813 | riov.iov_base = pAddress; |
| 1814 | riov.iov_len = nSize; |
| 1815 | nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0); |
| 1816 | if( nRead==nSize ){ |
| 1817 | db_unsave_encryption_key(); |
| 1818 | zSavedKey = p; |
| 1819 | savedKeySize = n; |
| 1820 | }else{ |
| 1821 | fossil_secure_free_page(p, n); |
| 1822 | fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu", |
| 1823 | nRead, nSize, pAddress, (unsigned long)processId); |
| 1824 | } |
| 1825 | #else |
| 1826 | fossil_secure_free_page(p, n); |
| 1827 | fossil_trace("db_read_saved_encryption_key_from_process unsupported"); |
| 1828 | #endif |
| 1829 | } |
| 1830 | } |
| 1831 | |
| 1832 | /* |
| 1833 | ** This function writes the saved database encryption key into the |
| 1834 | ** specified Fossil parent process. This is only necessary (or |
| 1835 | ** functional) on Windows or Linux. |
| 1836 | */ |
| 1837 | static void db_write_saved_encryption_key_to_process( |
| 1838 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1839 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1840 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1841 | ){ |
| 1842 | void *p = db_get_saved_encryption_key(); |
| 1843 | size_t n = db_get_saved_encryption_key_size(); |
| 1844 | size_t pageSize = 0; |
| 1845 | |
| 1846 | fossil_get_page_size(&pageSize); |
| 1847 | assert( pageSize>0 ); |
| 1848 | if( nSize>pageSize ){ |
| 1849 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| 1850 | } |
| 1851 | assert( p!=NULL ); |
| 1852 | assert( n==pageSize ); |
| 1853 | assert( n>=nSize ); |
| 1854 | { |
| 1855 | #if defined(_WIN32) |
| 1856 | HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, |
| 1857 | FALSE, processId); |
| 1858 | if( hProcess!=NULL ){ |
| 1859 | SIZE_T nWrite = 0; |
| 1860 | if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ |
| 1861 | CloseHandle(hProcess); |
| 1862 | if( nWrite!=nSize ){ |
| 1863 | fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu", |
| 1864 | nWrite, nSize, pAddress, processId); |
| 1865 | } |
| 1866 | }else{ |
| 1867 | CloseHandle(hProcess); |
| 1868 | fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize, |
| 1869 | pAddress, processId, GetLastError()); |
| 1870 | } |
| 1871 | }else{ |
| 1872 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1873 | } |
| 1874 | #elif defined(__linux__) |
| 1875 | ssize_t nWrite; |
| 1876 | struct iovec liov = {0}; |
| 1877 | struct iovec riov = {0}; |
| 1878 | liov.iov_base = p; |
| 1879 | liov.iov_len = n; |
| 1880 | riov.iov_base = pAddress; |
| 1881 | riov.iov_len = nSize; |
| 1882 | nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); |
| 1883 | if( nWrite!=nSize ){ |
| 1884 | fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu", |
| 1885 | nWrite, nSize, pAddress, (unsigned long)processId); |
| 1886 | } |
| 1887 | #else |
| 1888 | fossil_trace("db_write_saved_encryption_key_to_process unsupported"); |
| 1889 | #endif |
| 1890 | } |
| 1891 | } |
| 1892 | |
| 1893 | /* |
| 1894 | ** This function zeros the saved database encryption key in the specified |
| 1895 | ** Fossil parent process. This is only necessary (or functional) on |
| 1896 | ** Windows or Linux. |
| 1897 | */ |
| 1898 | static void db_zero_saved_encryption_key_in_process( |
| 1899 | PID_T processId, /* Identifier for Fossil parent process. */ |
| 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1902 | ){ |
| 1903 | void *p = NULL; |
| 1904 | size_t n = 0; |
| 1905 | size_t pageSize = 0; |
| 1906 | |
| 1907 | fossil_get_page_size(&pageSize); |
| 1908 | assert( pageSize>0 ); |
| 1909 | if( nSize>pageSize ){ |
| 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | |
| 1911 | } |
| 1912 | p = fossil_secure_alloc_page(&n); |
| 1913 | assert( p!=NULL ); |
| 1914 | assert( n==pageSize ); |
| 1915 | assert( n>=nSize ); |
| 1916 | { |
| 1917 | #if defined(_WIN32) |
| 1918 | HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE, |
| 1919 | FALSE, processId); |
| 1920 | if( hProcess!=NULL ){ |
| 1921 | SIZE_T nWrite = 0; |
| 1922 | if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){ |
| 1923 | CloseHandle(hProcess); |
| 1924 | fossil_secure_free_page(p, n); |
| 1925 | if( nWrite!=nSize ){ |
| 1926 | fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu", |
| 1927 | nWrite, nSize, pAddress, processId); |
| 1928 | } |
| 1929 | }else{ |
| 1930 | CloseHandle(hProcess); |
| 1931 | fossil_secure_free_page(p, n); |
| 1932 | fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize, |
| 1933 | pAddress, processId, GetLastError()); |
| 1934 | } |
| 1935 | }else{ |
| 1936 | fossil_secure_free_page(p, n); |
| 1937 | fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); |
| 1938 | } |
| 1939 | #elif defined(__linux__) |
| 1940 | ssize_t nWrite; |
| 1941 | struct iovec liov = {0}; |
| 1942 | struct iovec riov = {0}; |
| 1943 | liov.iov_base = p; |
| 1944 | liov.iov_len = n; |
| 1945 | riov.iov_base = pAddress; |
| 1946 | riov.iov_len = nSize; |
| 1947 | nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0); |
| 1948 | if( nWrite!=nSize ){ |
| 1949 | fossil_secure_free_page(p, n); |
| 1950 | fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu", |
| 1951 | nWrite, nSize, pAddress, (unsigned long)processId); |
| 1952 | } |
| 1953 | #else |
| 1954 | fossil_secure_free_page(p, n); |
| 1955 | fossil_trace("db_zero_saved_encryption_key_in_process unsupported"); |
| 1956 | #endif |
| 1957 | } |
| 1958 | } |
| 1959 | |
| 1960 | /* |
| 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1963 | ** address, and size (in bytes) of the database encryption key. This is |
| 1964 | ** only necessary (or functional) on Windows or Linux. |
| 1965 | */ |
| 1966 | static PID_T db_handle_saved_encryption_key_for_process_via_th1( |
| 1967 | const char *zConfig, /* The TH1 script to evaluate. */ |
| 1968 | int eType /* Non-zero to write key to parent process -OR- |
| 1969 | * zero to read it from the parent process. */ |
| 1970 | ){ |
| 1971 | PID_T processId = 0; |
| 1972 | int rc; |
| 1973 | char *zResult; |
| 1974 | char *zPwd = file_getcwd(0, 0); |
| 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | |
| 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1978 | if( rc!=TH_OK ){ |
| 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1980 | } |
| 1981 | if( zResult ){ |
| 1982 | LPVOID pAddress = NULL; |
| 1983 | SIZE_T nSize = 0; |
| 1984 | parse_pid_key_value(zResult, &processId, &pAddress, &nSize); |
| 1985 | if( eType==SEE_KEY_READ ){ |
| 1986 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 1987 | }else if( eType==SEE_KEY_WRITE ){ |
| 1988 | db_write_saved_encryption_key_to_process(processId, pAddress, nSize); |
| 1989 | }else if( eType==SEE_KEY_ZERO ){ |
| 1990 | db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); |
| 1991 | }else{ |
| 1992 | fossil_panic("unsupported SEE key operation %d", eType); |
| 1993 | } |
| 1994 | } |
| 1995 | file_chdir(zPwd, 0); |
| 1996 | fossil_free(zPwd); |
| 1997 | return processId; |
| 1998 | } |
| 1999 | |
| 2000 | /* |
| 2001 | ** This function sets the saved database encryption key to one that gets |
| 2002 | ** read from the specified Fossil parent process, if applicable. This is |
| 2003 | ** only necessary (or functional) on Windows or Linux. |
| 2004 | */ |
| 2005 | PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){ |
| 2006 | PID_T processId = 0; |
| 2007 | g.zPidKey = find_option("usepidkey",0,1); |
| 2008 | if( !g.zPidKey ){ |
| 2009 | g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY"); |
| 2010 | } |
| 2011 | if( g.zPidKey ){ |
| 2012 | LPVOID pAddress = NULL; |
| 2013 | SIZE_T nSize = 0; |
| 2014 | parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); |
| 2015 | if( eType==SEE_KEY_READ ){ |
| 2016 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 2017 | }else if( eType==SEE_KEY_WRITE ){ |
| 2018 | db_write_saved_encryption_key_to_process(processId, pAddress, nSize); |
| 2019 | }else if( eType==SEE_KEY_ZERO ){ |
| 2020 | db_zero_saved_encryption_key_in_process(processId, pAddress, nSize); |
| 2021 | }else{ |
| 2022 | fossil_panic("unsupported SEE key operation %d", eType); |
| 2023 | } |
| 2024 | }else{ |
| 2025 | const char *zSeeDbConfig = find_option("seedbcfg",0,1); |
| 2026 | if( !zSeeDbConfig ){ |
| 2027 | zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); |
| 2028 | } |
| 2029 | if( zSeeDbConfig ){ |
| 2030 | processId = db_handle_saved_encryption_key_for_process_via_th1( |
| 2031 | zSeeDbConfig, eType |
| 2032 | ); |
| 2033 | } |
| 2034 | } |
| 2035 | return processId; |
| 2036 | } |
| 2037 | #endif /* USE_SEE */ |
| 2038 | |
| 2039 | /* |
| 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 2041 | ** encrypted, then prompt for the database encryption key and return it |
| @@ -3294,10 +3630,11 @@ | |
| 3630 | noWarn = 1; |
| 3631 | } |
| 3632 | } |
| 3633 | blob_reset(&versionedPathname); |
| 3634 | if( found ){ |
| 3635 | blob_strip_comment_lines(&setting, &setting); |
| 3636 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3637 | ** on boolean properties */ |
| 3638 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3639 | } |
| 3640 | blob_reset(&setting); |
| @@ -4158,13 +4495,14 @@ | |
| 4495 | ** every time the backoffice runs. This can be used for debugging, |
| 4496 | ** to ensure that backoffice is running appropriately. |
| 4497 | */ |
| 4498 | /* |
| 4499 | ** SETTING: binary-glob width=40 versionable block-text |
| 4500 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4501 | ** that should be treated as "binary" for committing and merging |
| 4502 | ** purposes. Example: *.jpg,*.png The parsing rules are complex; |
| 4503 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4504 | */ |
| 4505 | #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) |
| 4506 | /* |
| 4507 | ** SETTING: case-sensitive boolean default=off |
| 4508 | ** If TRUE, the files whose names differ only in case |
| @@ -4182,14 +4520,14 @@ | |
| 4520 | ** TRUE for unix and FALSE for Cygwin, Mac and Windows. |
| 4521 | */ |
| 4522 | #endif |
| 4523 | /* |
| 4524 | ** SETTING: clean-glob width=40 versionable block-text |
| 4525 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4526 | ** that the "clean" command will delete without prompting or allowing |
| 4527 | ** undo. Example: *.a,*.o,*.so The parsing rules are complex; |
| 4528 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4529 | */ |
| 4530 | /* |
| 4531 | ** SETTING: clearsign boolean default=off |
| 4532 | ** When enabled, fossil will attempt to sign all commits |
| 4533 | ** with gpg. When disabled, commits will be unsigned. |
| @@ -4217,13 +4555,14 @@ | |
| 4555 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4556 | ** configured through the /setup_timeline web page. |
| 4557 | */ |
| 4558 | /* |
| 4559 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4560 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4561 | ** in which it is allowed to have CR, CR+LF or mixed line endings, |
| 4562 | ** suppressing Fossil's normal warning about this. Set it to "*" to |
| 4563 | ** disable CR+LF checking entirely. Example: *.md,*.txt |
| 4564 | ** The crnl-glob setting is a compatibility alias. |
| 4565 | */ |
| 4566 | /* |
| 4567 | ** SETTING: crnl-glob width=40 versionable block-text |
| 4568 | ** This is an alias for the crlf-glob setting. |
| @@ -4265,22 +4604,22 @@ | |
| 4604 | ** The value is an external command that will launch the |
| 4605 | ** text editor command used for check-in comments. |
| 4606 | */ |
| 4607 | /* |
| 4608 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4609 | ** The value is a list of pathnames parsed according to the same rules as |
| 4610 | ** the *-glob settings. On update and checkout commands, if no directory |
| 4611 | ** exists with that name, an empty directory will be be created, even if |
| 4612 | ** it must create one or more parent directories. |
| 4613 | */ |
| 4614 | /* |
| 4615 | ** SETTING: encoding-glob width=40 versionable block-text |
| 4616 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4617 | ** the "commit" command will ignore when issuing warnings about text files |
| 4618 | ** that may use another encoding than ASCII or UTF-8. Set to "*" to disable |
| 4619 | ** encoding checking. Example: *.md,*.txt The parsing rules are complex; |
| 4620 | ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4621 | */ |
| 4622 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| 4623 | /* |
| 4624 | ** SETTING: exec-rel-paths boolean default=on |
| 4625 | ** When executing certain external commands (e.g. diff and |
| @@ -4295,14 +4634,15 @@ | |
| 4634 | */ |
| 4635 | #endif |
| 4636 | |
| 4637 | /* |
| 4638 | ** SETTING: fileedit-glob width=40 block-text |
| 4639 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4640 | ** which are allowed to be edited using the /fileedit page. An empty list |
| 4641 | ** suppresses the feature. Example: *.md,*.txt The parsing rules are |
| 4642 | ** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4643 | ** Note that /fileedit cannot edit binary files, so the list should not |
| 4644 | ** contain any globs for, e.g., images or PDFs. |
| 4645 | */ |
| 4646 | /* |
| 4647 | ** SETTING: forbid-delta-manifests boolean default=off |
| 4648 | ** If enabled on a client, new delta manifests are prohibited on |
| @@ -4340,21 +4680,21 @@ | |
| 4680 | ** If true, then the Fossil web server will redirect unencrypted |
| 4681 | ** login screen requests to HTTPS. |
| 4682 | */ |
| 4683 | /* |
| 4684 | ** SETTING: ignore-glob width=40 versionable block-text |
| 4685 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4686 | ** the "add", "addremove", "clean", and "extras" commands will ignore. |
| 4687 | ** Example: *.log,notes.txt The parsing rules are complex; see |
| 4688 | ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4689 | */ |
| 4690 | /* |
| 4691 | ** SETTING: keep-glob width=40 versionable block-text |
| 4692 | ** The VALUE of this setting is a list of GLOB patterns matching files that |
| 4693 | ** the "clean" command must not delete. Example: build/precious.exe |
| 4694 | ** The parsing rules are complex; see |
| 4695 | ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax |
| 4696 | */ |
| 4697 | /* |
| 4698 | ** SETTING: localauth boolean default=off |
| 4699 | ** If enabled, require that HTTP connections from the loopback |
| 4700 | ** address (127.0.0.1) be authenticated by password. If false, |
| 4701 |
+5
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -732,11 +732,16 @@ | ||
| 732 | 732 | pre.textPlain { |
| 733 | 733 | white-space: pre-wrap; |
| 734 | 734 | word-wrap: break-word; |
| 735 | 735 | } |
| 736 | 736 | .statistics-report-graph-line { |
| 737 | + border: 2px solid #446979; | |
| 737 | 738 | background-color: #446979; |
| 739 | +} | |
| 740 | +.statistics-report-graph-extra { | |
| 741 | + border: 2px dashed #446979; | |
| 742 | + border-left-style: none; | |
| 738 | 743 | } |
| 739 | 744 | .statistics-report-table-events th { |
| 740 | 745 | padding: 0 1em 0 1em; |
| 741 | 746 | } |
| 742 | 747 | .statistics-report-table-events td { |
| 743 | 748 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -732,11 +732,16 @@ | |
| 732 | pre.textPlain { |
| 733 | white-space: pre-wrap; |
| 734 | word-wrap: break-word; |
| 735 | } |
| 736 | .statistics-report-graph-line { |
| 737 | background-color: #446979; |
| 738 | } |
| 739 | .statistics-report-table-events th { |
| 740 | padding: 0 1em 0 1em; |
| 741 | } |
| 742 | .statistics-report-table-events td { |
| 743 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -732,11 +732,16 @@ | |
| 732 | pre.textPlain { |
| 733 | white-space: pre-wrap; |
| 734 | word-wrap: break-word; |
| 735 | } |
| 736 | .statistics-report-graph-line { |
| 737 | border: 2px solid #446979; |
| 738 | background-color: #446979; |
| 739 | } |
| 740 | .statistics-report-graph-extra { |
| 741 | border: 2px dashed #446979; |
| 742 | border-left-style: none; |
| 743 | } |
| 744 | .statistics-report-table-events th { |
| 745 | padding: 0 1em 0 1em; |
| 746 | } |
| 747 | .statistics-report-table-events td { |
| 748 |
+5
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -732,11 +732,16 @@ | ||
| 732 | 732 | pre.textPlain { |
| 733 | 733 | white-space: pre-wrap; |
| 734 | 734 | word-wrap: break-word; |
| 735 | 735 | } |
| 736 | 736 | .statistics-report-graph-line { |
| 737 | + border: 2px solid #446979; | |
| 737 | 738 | background-color: #446979; |
| 739 | +} | |
| 740 | +.statistics-report-graph-extra { | |
| 741 | + border: 2px dashed #446979; | |
| 742 | + border-left-style: none; | |
| 738 | 743 | } |
| 739 | 744 | .statistics-report-table-events th { |
| 740 | 745 | padding: 0 1em 0 1em; |
| 741 | 746 | } |
| 742 | 747 | .statistics-report-table-events td { |
| 743 | 748 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -732,11 +732,16 @@ | |
| 732 | pre.textPlain { |
| 733 | white-space: pre-wrap; |
| 734 | word-wrap: break-word; |
| 735 | } |
| 736 | .statistics-report-graph-line { |
| 737 | background-color: #446979; |
| 738 | } |
| 739 | .statistics-report-table-events th { |
| 740 | padding: 0 1em 0 1em; |
| 741 | } |
| 742 | .statistics-report-table-events td { |
| 743 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -732,11 +732,16 @@ | |
| 732 | pre.textPlain { |
| 733 | white-space: pre-wrap; |
| 734 | word-wrap: break-word; |
| 735 | } |
| 736 | .statistics-report-graph-line { |
| 737 | border: 2px solid #446979; |
| 738 | background-color: #446979; |
| 739 | } |
| 740 | .statistics-report-graph-extra { |
| 741 | border: 2px dashed #446979; |
| 742 | border-left-style: none; |
| 743 | } |
| 744 | .statistics-report-table-events th { |
| 745 | padding: 0 1em 0 1em; |
| 746 | } |
| 747 | .statistics-report-table-events td { |
| 748 |
+1
-1
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -627,11 +627,11 @@ | ||
| 627 | 627 | if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; |
| 628 | 628 | if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; |
| 629 | 629 | if( fUBg ) tmFlags |= TIMELINE_UCOLOR; |
| 630 | 630 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 631 | 631 | db_finalize(&q); |
| 632 | - @ <br /> | |
| 632 | + @ <br> | |
| 633 | 633 | style_finish_page(); |
| 634 | 634 | } |
| 635 | 635 | |
| 636 | 636 | #if INTERFACE |
| 637 | 637 | /* Flag parameters to compute_uses_file() */ |
| 638 | 638 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -627,11 +627,11 @@ | |
| 627 | if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; |
| 628 | if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; |
| 629 | if( fUBg ) tmFlags |= TIMELINE_UCOLOR; |
| 630 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 631 | db_finalize(&q); |
| 632 | @ <br /> |
| 633 | style_finish_page(); |
| 634 | } |
| 635 | |
| 636 | #if INTERFACE |
| 637 | /* Flag parameters to compute_uses_file() */ |
| 638 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -627,11 +627,11 @@ | |
| 627 | if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; |
| 628 | if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; |
| 629 | if( fUBg ) tmFlags |= TIMELINE_UCOLOR; |
| 630 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 631 | db_finalize(&q); |
| 632 | @ <br> |
| 633 | style_finish_page(); |
| 634 | } |
| 635 | |
| 636 | #if INTERFACE |
| 637 | /* Flag parameters to compute_uses_file() */ |
| 638 |
+1
-1
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -3636,11 +3636,11 @@ | ||
| 3636 | 3636 | @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a> |
| 3637 | 3637 | @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a> |
| 3638 | 3638 | @ </span> |
| 3639 | 3639 | } |
| 3640 | 3640 | @ </ol> |
| 3641 | - @ <hr /> | |
| 3641 | + @ <hr> | |
| 3642 | 3642 | @ </div> |
| 3643 | 3643 | |
| 3644 | 3644 | if( !ann.bMoreToDo ){ |
| 3645 | 3645 | assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */ |
| 3646 | 3646 | @ <h2>Origin for each line in |
| 3647 | 3647 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3636,11 +3636,11 @@ | |
| 3636 | @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a> |
| 3637 | @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a> |
| 3638 | @ </span> |
| 3639 | } |
| 3640 | @ </ol> |
| 3641 | @ <hr /> |
| 3642 | @ </div> |
| 3643 | |
| 3644 | if( !ann.bMoreToDo ){ |
| 3645 | assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */ |
| 3646 | @ <h2>Origin for each line in |
| 3647 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3636,11 +3636,11 @@ | |
| 3636 | @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a> |
| 3637 | @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a> |
| 3638 | @ </span> |
| 3639 | } |
| 3640 | @ </ol> |
| 3641 | @ <hr> |
| 3642 | @ </div> |
| 3643 | |
| 3644 | if( !ann.bMoreToDo ){ |
| 3645 | assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */ |
| 3646 | @ <h2>Origin for each line in |
| 3647 |
+3
-3
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -550,11 +550,11 @@ | ||
| 550 | 550 | ** Display help for all commands based on provided flags. |
| 551 | 551 | */ |
| 552 | 552 | static void display_all_help(int mask, int useHtml, int rawOut){ |
| 553 | 553 | int i; |
| 554 | 554 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 555 | - int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help strings -> commands*/ | |
| 555 | + int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ | |
| 556 | 556 | if( useHtml ) fossil_print("<!--\n"); |
| 557 | 557 | fossil_print("Help text for:\n"); |
| 558 | 558 | if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n"); |
| 559 | 559 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 560 | 560 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| @@ -859,11 +859,11 @@ | ||
| 859 | 859 | } |
| 860 | 860 | } |
| 861 | 861 | }else{ |
| 862 | 862 | int i; |
| 863 | 863 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 864 | - int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help str -> commands */ | |
| 864 | + int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ | |
| 865 | 865 | style_header("Help"); |
| 866 | 866 | |
| 867 | 867 | @ <a name='commands'></a> |
| 868 | 868 | @ <h1>Available commands:</h1> |
| 869 | 869 | @ <div class="columns" style="column-width: 12ex;"> |
| @@ -966,11 +966,11 @@ | ||
| 966 | 966 | ** Show all help text on a single page. Useful for proof-reading. |
| 967 | 967 | */ |
| 968 | 968 | void test_all_help_page(void){ |
| 969 | 969 | int i; |
| 970 | 970 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 971 | - int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help strings -> commands*/ | |
| 971 | + int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ | |
| 972 | 972 | Blob buf; |
| 973 | 973 | blob_init(&buf,0,0); |
| 974 | 974 | style_set_current_feature("test"); |
| 975 | 975 | style_header("All Help Text"); |
| 976 | 976 | @ <dl> |
| 977 | 977 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -550,11 +550,11 @@ | |
| 550 | ** Display help for all commands based on provided flags. |
| 551 | */ |
| 552 | static void display_all_help(int mask, int useHtml, int rawOut){ |
| 553 | int i; |
| 554 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 555 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help strings -> commands*/ |
| 556 | if( useHtml ) fossil_print("<!--\n"); |
| 557 | fossil_print("Help text for:\n"); |
| 558 | if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n"); |
| 559 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 560 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| @@ -859,11 +859,11 @@ | |
| 859 | } |
| 860 | } |
| 861 | }else{ |
| 862 | int i; |
| 863 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 864 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help str -> commands */ |
| 865 | style_header("Help"); |
| 866 | |
| 867 | @ <a name='commands'></a> |
| 868 | @ <h1>Available commands:</h1> |
| 869 | @ <div class="columns" style="column-width: 12ex;"> |
| @@ -966,11 +966,11 @@ | |
| 966 | ** Show all help text on a single page. Useful for proof-reading. |
| 967 | */ |
| 968 | void test_all_help_page(void){ |
| 969 | int i; |
| 970 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 971 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {0};/* Help strings -> commands*/ |
| 972 | Blob buf; |
| 973 | blob_init(&buf,0,0); |
| 974 | style_set_current_feature("test"); |
| 975 | style_header("All Help Text"); |
| 976 | @ <dl> |
| 977 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -550,11 +550,11 @@ | |
| 550 | ** Display help for all commands based on provided flags. |
| 551 | */ |
| 552 | static void display_all_help(int mask, int useHtml, int rawOut){ |
| 553 | int i; |
| 554 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 555 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ |
| 556 | if( useHtml ) fossil_print("<!--\n"); |
| 557 | fossil_print("Help text for:\n"); |
| 558 | if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n"); |
| 559 | if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n"); |
| 560 | if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n"); |
| @@ -859,11 +859,11 @@ | |
| 859 | } |
| 860 | } |
| 861 | }else{ |
| 862 | int i; |
| 863 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 864 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 865 | style_header("Help"); |
| 866 | |
| 867 | @ <a name='commands'></a> |
| 868 | @ <h1>Available commands:</h1> |
| 869 | @ <div class="columns" style="column-width: 12ex;"> |
| @@ -966,11 +966,11 @@ | |
| 966 | ** Show all help text on a single page. Useful for proof-reading. |
| 967 | */ |
| 968 | void test_all_help_page(void){ |
| 969 | int i; |
| 970 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */ |
| 971 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/ |
| 972 | Blob buf; |
| 973 | blob_init(&buf,0,0); |
| 974 | style_set_current_feature("test"); |
| 975 | style_header("All Help Text"); |
| 976 | @ <dl> |
| 977 |
+10
-10
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -212,11 +212,11 @@ | ||
| 212 | 212 | } |
| 213 | 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | 215 | blob_reset(&comment); |
| 216 | 216 | @ </div> |
| 217 | - @ </blockquote><hr /> | |
| 217 | + @ </blockquote><hr> | |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | ||
| 228 | 228 | } |
| 229 | 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | 230 | " FROM tag" |
| 231 | 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | 232 | zId); |
| 233 | - attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>"); | |
| 233 | + attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); | |
| 234 | 234 | document_emit_js(); |
| 235 | 235 | style_finish_page(); |
| 236 | 236 | manifest_destroy(pTNote); |
| 237 | 237 | } |
| 238 | 238 | |
| @@ -513,26 +513,26 @@ | ||
| 513 | 513 | @ <blockquote> |
| 514 | 514 | blob_init(&event, 0, 0); |
| 515 | 515 | blob_append(&event, zBody, -1); |
| 516 | 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | - @ </blockquote><hr /> | |
| 518 | + @ </blockquote><hr> | |
| 519 | 519 | blob_reset(&event); |
| 520 | 520 | } |
| 521 | 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | 522 | if( z[0]=='\n' ) n++; |
| 523 | 523 | } |
| 524 | 524 | if( n<20 ) n = 20; |
| 525 | 525 | if( n>40 ) n = 40; |
| 526 | 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | 527 | login_insert_csrf_secret(); |
| 528 | - @ <input type="hidden" name="name" value="%h(zId)" /> | |
| 528 | + @ <input type="hidden" name="name" value="%h(zId)"> | |
| 529 | 529 | @ <table border="0" cellspacing="10"> |
| 530 | 530 | |
| 531 | 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | 532 | @ <td valign="top"> |
| 533 | - @ <input type="text" name="t" size="25" value="%h(zETime)" /> | |
| 533 | + @ <input type="text" name="t" size="25" value="%h(zETime)"> | |
| 534 | 534 | @ </td></tr> |
| 535 | 535 | |
| 536 | 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | 537 | @ <td valign="top"> |
| 538 | 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | ||
| 539 | 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | 540 | @ </td></tr> |
| 541 | 541 | |
| 542 | 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | 543 | @ <td valign="top"> |
| 544 | - @ <input type='checkbox' name='newclr'%s(zClrFlag) /> | |
| 544 | + @ <input type='checkbox' name='newclr'%s(zClrFlag)> | |
| 545 | 545 | @ Use custom color: \ |
| 546 | 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | 547 | @ </td></tr> |
| 548 | 548 | |
| 549 | 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | 550 | @ <td valign="top"> |
| 551 | - @ <input type="text" name="g" size="40" value="%h(zTags)" /> | |
| 551 | + @ <input type="text" name="g" size="40" value="%h(zTags)"> | |
| 552 | 552 | @ </td></tr> |
| 553 | 553 | |
| 554 | 554 | @ <tr><th align="right" valign="top">\ |
| 555 | 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | ||
| 562 | 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | 564 | @ </td></tr> |
| 565 | 565 | |
| 566 | 566 | @ <tr><td colspan="2"> |
| 567 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 568 | - @ <input type="submit" name="preview" value="Preview" /> | |
| 567 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 568 | + @ <input type="submit" name="preview" value="Preview"> | |
| 569 | 569 | if( P("preview") ){ |
| 570 | - @ <input type="submit" name="submit" value="Submit" /> | |
| 570 | + @ <input type="submit" name="submit" value="Submit"> | |
| 571 | 571 | } |
| 572 | 572 | @ </td></tr></table> |
| 573 | 573 | @ </div></form> |
| 574 | 574 | style_finish_page(); |
| 575 | 575 | } |
| 576 | 576 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | } |
| 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | blob_reset(&comment); |
| 216 | @ </div> |
| 217 | @ </blockquote><hr /> |
| 218 | } |
| 219 | |
| 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | |
| 228 | } |
| 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | " FROM tag" |
| 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | zId); |
| 233 | attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>"); |
| 234 | document_emit_js(); |
| 235 | style_finish_page(); |
| 236 | manifest_destroy(pTNote); |
| 237 | } |
| 238 | |
| @@ -513,26 +513,26 @@ | |
| 513 | @ <blockquote> |
| 514 | blob_init(&event, 0, 0); |
| 515 | blob_append(&event, zBody, -1); |
| 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | @ </blockquote><hr /> |
| 519 | blob_reset(&event); |
| 520 | } |
| 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | if( z[0]=='\n' ) n++; |
| 523 | } |
| 524 | if( n<20 ) n = 20; |
| 525 | if( n>40 ) n = 40; |
| 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | login_insert_csrf_secret(); |
| 528 | @ <input type="hidden" name="name" value="%h(zId)" /> |
| 529 | @ <table border="0" cellspacing="10"> |
| 530 | |
| 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | @ <td valign="top"> |
| 533 | @ <input type="text" name="t" size="25" value="%h(zETime)" /> |
| 534 | @ </td></tr> |
| 535 | |
| 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | @ <td valign="top"> |
| 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | |
| 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | @ </td></tr> |
| 541 | |
| 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | @ <td valign="top"> |
| 544 | @ <input type='checkbox' name='newclr'%s(zClrFlag) /> |
| 545 | @ Use custom color: \ |
| 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | @ </td></tr> |
| 548 | |
| 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | @ <td valign="top"> |
| 551 | @ <input type="text" name="g" size="40" value="%h(zTags)" /> |
| 552 | @ </td></tr> |
| 553 | |
| 554 | @ <tr><th align="right" valign="top">\ |
| 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | |
| 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | @ </td></tr> |
| 565 | |
| 566 | @ <tr><td colspan="2"> |
| 567 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 568 | @ <input type="submit" name="preview" value="Preview" /> |
| 569 | if( P("preview") ){ |
| 570 | @ <input type="submit" name="submit" value="Submit" /> |
| 571 | } |
| 572 | @ </td></tr></table> |
| 573 | @ </div></form> |
| 574 | style_finish_page(); |
| 575 | } |
| 576 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | } |
| 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | blob_reset(&comment); |
| 216 | @ </div> |
| 217 | @ </blockquote><hr> |
| 218 | } |
| 219 | |
| 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | |
| 228 | } |
| 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | " FROM tag" |
| 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | zId); |
| 233 | attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); |
| 234 | document_emit_js(); |
| 235 | style_finish_page(); |
| 236 | manifest_destroy(pTNote); |
| 237 | } |
| 238 | |
| @@ -513,26 +513,26 @@ | |
| 513 | @ <blockquote> |
| 514 | blob_init(&event, 0, 0); |
| 515 | blob_append(&event, zBody, -1); |
| 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | @ </blockquote><hr> |
| 519 | blob_reset(&event); |
| 520 | } |
| 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | if( z[0]=='\n' ) n++; |
| 523 | } |
| 524 | if( n<20 ) n = 20; |
| 525 | if( n>40 ) n = 40; |
| 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | login_insert_csrf_secret(); |
| 528 | @ <input type="hidden" name="name" value="%h(zId)"> |
| 529 | @ <table border="0" cellspacing="10"> |
| 530 | |
| 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | @ <td valign="top"> |
| 533 | @ <input type="text" name="t" size="25" value="%h(zETime)"> |
| 534 | @ </td></tr> |
| 535 | |
| 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | @ <td valign="top"> |
| 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | |
| 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | @ </td></tr> |
| 541 | |
| 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | @ <td valign="top"> |
| 544 | @ <input type='checkbox' name='newclr'%s(zClrFlag)> |
| 545 | @ Use custom color: \ |
| 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | @ </td></tr> |
| 548 | |
| 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | @ <td valign="top"> |
| 551 | @ <input type="text" name="g" size="40" value="%h(zTags)"> |
| 552 | @ </td></tr> |
| 553 | |
| 554 | @ <tr><th align="right" valign="top">\ |
| 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | |
| 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | @ </td></tr> |
| 565 | |
| 566 | @ <tr><td colspan="2"> |
| 567 | @ <input type="submit" name="cancel" value="Cancel"> |
| 568 | @ <input type="submit" name="preview" value="Preview"> |
| 569 | if( P("preview") ){ |
| 570 | @ <input type="submit" name="submit" value="Submit"> |
| 571 | } |
| 572 | @ </td></tr></table> |
| 573 | @ </div></form> |
| 574 | style_finish_page(); |
| 575 | } |
| 576 |
+10
-10
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -212,11 +212,11 @@ | ||
| 212 | 212 | } |
| 213 | 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | 215 | blob_reset(&comment); |
| 216 | 216 | @ </div> |
| 217 | - @ </blockquote><hr /> | |
| 217 | + @ </blockquote><hr> | |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | ||
| 228 | 228 | } |
| 229 | 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | 230 | " FROM tag" |
| 231 | 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | 232 | zId); |
| 233 | - attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>"); | |
| 233 | + attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); | |
| 234 | 234 | document_emit_js(); |
| 235 | 235 | style_finish_page(); |
| 236 | 236 | manifest_destroy(pTNote); |
| 237 | 237 | } |
| 238 | 238 | |
| @@ -513,26 +513,26 @@ | ||
| 513 | 513 | @ <blockquote> |
| 514 | 514 | blob_init(&event, 0, 0); |
| 515 | 515 | blob_append(&event, zBody, -1); |
| 516 | 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | - @ </blockquote><hr /> | |
| 518 | + @ </blockquote><hr> | |
| 519 | 519 | blob_reset(&event); |
| 520 | 520 | } |
| 521 | 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | 522 | if( z[0]=='\n' ) n++; |
| 523 | 523 | } |
| 524 | 524 | if( n<20 ) n = 20; |
| 525 | 525 | if( n>40 ) n = 40; |
| 526 | 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | 527 | login_insert_csrf_secret(); |
| 528 | - @ <input type="hidden" name="name" value="%h(zId)" /> | |
| 528 | + @ <input type="hidden" name="name" value="%h(zId)"> | |
| 529 | 529 | @ <table border="0" cellspacing="10"> |
| 530 | 530 | |
| 531 | 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | 532 | @ <td valign="top"> |
| 533 | - @ <input type="text" name="t" size="25" value="%h(zETime)" /> | |
| 533 | + @ <input type="text" name="t" size="25" value="%h(zETime)"> | |
| 534 | 534 | @ </td></tr> |
| 535 | 535 | |
| 536 | 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | 537 | @ <td valign="top"> |
| 538 | 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | ||
| 539 | 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | 540 | @ </td></tr> |
| 541 | 541 | |
| 542 | 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | 543 | @ <td valign="top"> |
| 544 | - @ <input type='checkbox' name='newclr'%s(zClrFlag) /> | |
| 544 | + @ <input type='checkbox' name='newclr'%s(zClrFlag)> | |
| 545 | 545 | @ Use custom color: \ |
| 546 | 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | 547 | @ </td></tr> |
| 548 | 548 | |
| 549 | 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | 550 | @ <td valign="top"> |
| 551 | - @ <input type="text" name="g" size="40" value="%h(zTags)" /> | |
| 551 | + @ <input type="text" name="g" size="40" value="%h(zTags)"> | |
| 552 | 552 | @ </td></tr> |
| 553 | 553 | |
| 554 | 554 | @ <tr><th align="right" valign="top">\ |
| 555 | 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | ||
| 562 | 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | 564 | @ </td></tr> |
| 565 | 565 | |
| 566 | 566 | @ <tr><td colspan="2"> |
| 567 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 568 | - @ <input type="submit" name="preview" value="Preview" /> | |
| 567 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 568 | + @ <input type="submit" name="preview" value="Preview"> | |
| 569 | 569 | if( P("preview") ){ |
| 570 | - @ <input type="submit" name="submit" value="Submit" /> | |
| 570 | + @ <input type="submit" name="submit" value="Submit"> | |
| 571 | 571 | } |
| 572 | 572 | @ </td></tr></table> |
| 573 | 573 | @ </div></form> |
| 574 | 574 | style_finish_page(); |
| 575 | 575 | } |
| 576 | 576 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | } |
| 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | blob_reset(&comment); |
| 216 | @ </div> |
| 217 | @ </blockquote><hr /> |
| 218 | } |
| 219 | |
| 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | |
| 228 | } |
| 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | " FROM tag" |
| 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | zId); |
| 233 | attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>"); |
| 234 | document_emit_js(); |
| 235 | style_finish_page(); |
| 236 | manifest_destroy(pTNote); |
| 237 | } |
| 238 | |
| @@ -513,26 +513,26 @@ | |
| 513 | @ <blockquote> |
| 514 | blob_init(&event, 0, 0); |
| 515 | blob_append(&event, zBody, -1); |
| 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | @ </blockquote><hr /> |
| 519 | blob_reset(&event); |
| 520 | } |
| 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | if( z[0]=='\n' ) n++; |
| 523 | } |
| 524 | if( n<20 ) n = 20; |
| 525 | if( n>40 ) n = 40; |
| 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | login_insert_csrf_secret(); |
| 528 | @ <input type="hidden" name="name" value="%h(zId)" /> |
| 529 | @ <table border="0" cellspacing="10"> |
| 530 | |
| 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | @ <td valign="top"> |
| 533 | @ <input type="text" name="t" size="25" value="%h(zETime)" /> |
| 534 | @ </td></tr> |
| 535 | |
| 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | @ <td valign="top"> |
| 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | |
| 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | @ </td></tr> |
| 541 | |
| 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | @ <td valign="top"> |
| 544 | @ <input type='checkbox' name='newclr'%s(zClrFlag) /> |
| 545 | @ Use custom color: \ |
| 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | @ </td></tr> |
| 548 | |
| 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | @ <td valign="top"> |
| 551 | @ <input type="text" name="g" size="40" value="%h(zTags)" /> |
| 552 | @ </td></tr> |
| 553 | |
| 554 | @ <tr><th align="right" valign="top">\ |
| 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | |
| 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | @ </td></tr> |
| 565 | |
| 566 | @ <tr><td colspan="2"> |
| 567 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 568 | @ <input type="submit" name="preview" value="Preview" /> |
| 569 | if( P("preview") ){ |
| 570 | @ <input type="submit" name="submit" value="Submit" /> |
| 571 | } |
| 572 | @ </td></tr></table> |
| 573 | @ </div></form> |
| 574 | style_finish_page(); |
| 575 | } |
| 576 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -212,11 +212,11 @@ | |
| 212 | } |
| 213 | blob_init(&comment, pTNote->zComment, -1); |
| 214 | wiki_convert(&comment, 0, WIKI_INLINE); |
| 215 | blob_reset(&comment); |
| 216 | @ </div> |
| 217 | @ </blockquote><hr> |
| 218 | } |
| 219 | |
| 220 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 221 | wiki_convert(&fullbody, 0, 0); |
| 222 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| @@ -228,11 +228,11 @@ | |
| 228 | } |
| 229 | zFullId = db_text(0, "SELECT SUBSTR(tagname,7)" |
| 230 | " FROM tag" |
| 231 | " WHERE tagname GLOB 'event-%q*'", |
| 232 | zId); |
| 233 | attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>"); |
| 234 | document_emit_js(); |
| 235 | style_finish_page(); |
| 236 | manifest_destroy(pTNote); |
| 237 | } |
| 238 | |
| @@ -513,26 +513,26 @@ | |
| 513 | @ <blockquote> |
| 514 | blob_init(&event, 0, 0); |
| 515 | blob_append(&event, zBody, -1); |
| 516 | safe_html_context(DOCSRC_WIKI); |
| 517 | wiki_render_by_mimetype(&event, zMimetype); |
| 518 | @ </blockquote><hr> |
| 519 | blob_reset(&event); |
| 520 | } |
| 521 | for(n=2, z=zBody; z[0]; z++){ |
| 522 | if( z[0]=='\n' ) n++; |
| 523 | } |
| 524 | if( n<20 ) n = 20; |
| 525 | if( n>40 ) n = 40; |
| 526 | @ <form method="post" action="%R/technoteedit"><div> |
| 527 | login_insert_csrf_secret(); |
| 528 | @ <input type="hidden" name="name" value="%h(zId)"> |
| 529 | @ <table border="0" cellspacing="10"> |
| 530 | |
| 531 | @ <tr><th align="right" valign="top">Timestamp (UTC):</th> |
| 532 | @ <td valign="top"> |
| 533 | @ <input type="text" name="t" size="25" value="%h(zETime)"> |
| 534 | @ </td></tr> |
| 535 | |
| 536 | @ <tr><th align="right" valign="top">Timeline Comment:</th> |
| 537 | @ <td valign="top"> |
| 538 | @ <textarea name="c" class="technoteedit" cols="80" |
| @@ -539,18 +539,18 @@ | |
| 539 | @ rows="3" wrap="virtual">%h(zComment)</textarea> |
| 540 | @ </td></tr> |
| 541 | |
| 542 | @ <tr><th align="right" valign="top">Timeline Background Color:</th> |
| 543 | @ <td valign="top"> |
| 544 | @ <input type='checkbox' name='newclr'%s(zClrFlag)> |
| 545 | @ Use custom color: \ |
| 546 | @ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'> |
| 547 | @ </td></tr> |
| 548 | |
| 549 | @ <tr><th align="right" valign="top">Tags:</th> |
| 550 | @ <td valign="top"> |
| 551 | @ <input type="text" name="g" size="40" value="%h(zTags)"> |
| 552 | @ </td></tr> |
| 553 | |
| 554 | @ <tr><th align="right" valign="top">\ |
| 555 | @ %z(href("%R/markup_help"))Markup Style</a>:</th> |
| 556 | @ <td valign="top"> |
| @@ -562,14 +562,14 @@ | |
| 562 | @ <textarea name="w" class="technoteedit" cols="80" |
| 563 | @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> |
| 564 | @ </td></tr> |
| 565 | |
| 566 | @ <tr><td colspan="2"> |
| 567 | @ <input type="submit" name="cancel" value="Cancel"> |
| 568 | @ <input type="submit" name="preview" value="Preview"> |
| 569 | if( P("preview") ){ |
| 570 | @ <input type="submit" name="submit" value="Submit"> |
| 571 | } |
| 572 | @ </td></tr></table> |
| 573 | @ </div></form> |
| 574 | style_finish_page(); |
| 575 | } |
| 576 |
+1
| --- src/extcgi.c | ||
| +++ src/extcgi.c | ||
| @@ -49,10 +49,11 @@ | ||
| 49 | 49 | "FOSSIL_USER", |
| 50 | 50 | "GATEWAY_INTERFACE", |
| 51 | 51 | "HTTPS", |
| 52 | 52 | "HTTP_ACCEPT", |
| 53 | 53 | /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ |
| 54 | + "HTTP_ACCEPT_LANGUAGE", | |
| 54 | 55 | "HTTP_COOKIE", |
| 55 | 56 | "HTTP_HOST", |
| 56 | 57 | "HTTP_IF_MODIFIED_SINCE", |
| 57 | 58 | "HTTP_IF_NONE_MATCH", |
| 58 | 59 | "HTTP_REFERER", |
| 59 | 60 |
| --- src/extcgi.c | |
| +++ src/extcgi.c | |
| @@ -49,10 +49,11 @@ | |
| 49 | "FOSSIL_USER", |
| 50 | "GATEWAY_INTERFACE", |
| 51 | "HTTPS", |
| 52 | "HTTP_ACCEPT", |
| 53 | /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ |
| 54 | "HTTP_COOKIE", |
| 55 | "HTTP_HOST", |
| 56 | "HTTP_IF_MODIFIED_SINCE", |
| 57 | "HTTP_IF_NONE_MATCH", |
| 58 | "HTTP_REFERER", |
| 59 |
| --- src/extcgi.c | |
| +++ src/extcgi.c | |
| @@ -49,10 +49,11 @@ | |
| 49 | "FOSSIL_USER", |
| 50 | "GATEWAY_INTERFACE", |
| 51 | "HTTPS", |
| 52 | "HTTP_ACCEPT", |
| 53 | /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ |
| 54 | "HTTP_ACCEPT_LANGUAGE", |
| 55 | "HTTP_COOKIE", |
| 56 | "HTTP_HOST", |
| 57 | "HTTP_IF_MODIFIED_SINCE", |
| 58 | "HTTP_IF_NONE_MATCH", |
| 59 | "HTTP_REFERER", |
| 60 |
+43
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -2277,10 +2277,53 @@ | ||
| 2277 | 2277 | int i; |
| 2278 | 2278 | for(i=2; i<g.argc; i++){ |
| 2279 | 2279 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 2280 | 2280 | } |
| 2281 | 2281 | } |
| 2282 | + | |
| 2283 | +/* | |
| 2284 | +** Returns non-zero if the specified file extension belongs to a Fossil | |
| 2285 | +** repository file. | |
| 2286 | +*/ | |
| 2287 | +int file_is_repository_extension(const char *zPath){ | |
| 2288 | + if( fossil_strcmp(zPath, ".fossil")==0 ) return 1; | |
| 2289 | +#if USE_SEE | |
| 2290 | + if( fossil_strcmp(zPath, ".efossil")==0 ) return 1; | |
| 2291 | +#endif | |
| 2292 | + return 0; | |
| 2293 | +} | |
| 2294 | + | |
| 2295 | +/* | |
| 2296 | +** Returns non-zero if the specified path appears to match a file extension | |
| 2297 | +** that should belong to a Fossil repository file. | |
| 2298 | +*/ | |
| 2299 | +int file_contains_repository_extension(const char *zPath){ | |
| 2300 | + if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1; | |
| 2301 | +#if USE_SEE | |
| 2302 | + if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1; | |
| 2303 | +#endif | |
| 2304 | + return 0; | |
| 2305 | +} | |
| 2306 | + | |
| 2307 | +/* | |
| 2308 | +** Returns non-zero if the specified path ends with a file extension that | |
| 2309 | +** should belong to a Fossil repository file. | |
| 2310 | +*/ | |
| 2311 | +int file_ends_with_repository_extension(const char *zPath, int bQual){ | |
| 2312 | + if( bQual ){ | |
| 2313 | + if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1; | |
| 2314 | +#if USE_SEE | |
| 2315 | + if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1; | |
| 2316 | +#endif | |
| 2317 | + }else{ | |
| 2318 | + if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1; | |
| 2319 | +#if USE_SEE | |
| 2320 | + if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1; | |
| 2321 | +#endif | |
| 2322 | + } | |
| 2323 | + return 0; | |
| 2324 | +} | |
| 2282 | 2325 | |
| 2283 | 2326 | /* |
| 2284 | 2327 | ** Remove surplus "/" characters from the beginning of a full pathname. |
| 2285 | 2328 | ** Extra leading "/" characters are benign on unix. But on Windows |
| 2286 | 2329 | ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" |
| 2287 | 2330 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -2277,10 +2277,53 @@ | |
| 2277 | int i; |
| 2278 | for(i=2; i<g.argc; i++){ |
| 2279 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 2280 | } |
| 2281 | } |
| 2282 | |
| 2283 | /* |
| 2284 | ** Remove surplus "/" characters from the beginning of a full pathname. |
| 2285 | ** Extra leading "/" characters are benign on unix. But on Windows |
| 2286 | ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" |
| 2287 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -2277,10 +2277,53 @@ | |
| 2277 | int i; |
| 2278 | for(i=2; i<g.argc; i++){ |
| 2279 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 2280 | } |
| 2281 | } |
| 2282 | |
| 2283 | /* |
| 2284 | ** Returns non-zero if the specified file extension belongs to a Fossil |
| 2285 | ** repository file. |
| 2286 | */ |
| 2287 | int file_is_repository_extension(const char *zPath){ |
| 2288 | if( fossil_strcmp(zPath, ".fossil")==0 ) return 1; |
| 2289 | #if USE_SEE |
| 2290 | if( fossil_strcmp(zPath, ".efossil")==0 ) return 1; |
| 2291 | #endif |
| 2292 | return 0; |
| 2293 | } |
| 2294 | |
| 2295 | /* |
| 2296 | ** Returns non-zero if the specified path appears to match a file extension |
| 2297 | ** that should belong to a Fossil repository file. |
| 2298 | */ |
| 2299 | int file_contains_repository_extension(const char *zPath){ |
| 2300 | if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1; |
| 2301 | #if USE_SEE |
| 2302 | if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1; |
| 2303 | #endif |
| 2304 | return 0; |
| 2305 | } |
| 2306 | |
| 2307 | /* |
| 2308 | ** Returns non-zero if the specified path ends with a file extension that |
| 2309 | ** should belong to a Fossil repository file. |
| 2310 | */ |
| 2311 | int file_ends_with_repository_extension(const char *zPath, int bQual){ |
| 2312 | if( bQual ){ |
| 2313 | if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1; |
| 2314 | #if USE_SEE |
| 2315 | if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1; |
| 2316 | #endif |
| 2317 | }else{ |
| 2318 | if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1; |
| 2319 | #if USE_SEE |
| 2320 | if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1; |
| 2321 | #endif |
| 2322 | } |
| 2323 | return 0; |
| 2324 | } |
| 2325 | |
| 2326 | /* |
| 2327 | ** Remove surplus "/" characters from the beginning of a full pathname. |
| 2328 | ** Extra leading "/" characters are benign on unix. But on Windows |
| 2329 | ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" |
| 2330 |
+2
-2
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -767,11 +767,11 @@ | ||
| 767 | 767 | @ </span></span> |
| 768 | 768 | } |
| 769 | 769 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 770 | 770 | int ii; |
| 771 | 771 | char *zAncLink; |
| 772 | - @ <br />fid=%d(frid) \ | |
| 772 | + @ <br>fid=%d(frid) \ | |
| 773 | 773 | @ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \ |
| 774 | 774 | @ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \ |
| 775 | 775 | @ pfnid=%d(pfnid) mxfnid=%d(mxfnid) |
| 776 | 776 | if( nParent>0 ){ |
| 777 | 777 | @ parents=%lld(aParent[0]) |
| @@ -930,11 +930,11 @@ | ||
| 930 | 930 | mid |
| 931 | 931 | ); |
| 932 | 932 | @ <h1>MLINK table for check-in %h(zCI)</h1> |
| 933 | 933 | render_checkin_context(mid, 0, 1, 0); |
| 934 | 934 | style_table_sorter(); |
| 935 | - @ <hr /> | |
| 935 | + @ <hr> | |
| 936 | 936 | @ <div class='brlist'> |
| 937 | 937 | @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'> |
| 938 | 938 | @ <thead><tr> |
| 939 | 939 | @ <th>File</th> |
| 940 | 940 | @ <th>Parent<br>Check-in</th> |
| 941 | 941 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -767,11 +767,11 @@ | |
| 767 | @ </span></span> |
| 768 | } |
| 769 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 770 | int ii; |
| 771 | char *zAncLink; |
| 772 | @ <br />fid=%d(frid) \ |
| 773 | @ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \ |
| 774 | @ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \ |
| 775 | @ pfnid=%d(pfnid) mxfnid=%d(mxfnid) |
| 776 | if( nParent>0 ){ |
| 777 | @ parents=%lld(aParent[0]) |
| @@ -930,11 +930,11 @@ | |
| 930 | mid |
| 931 | ); |
| 932 | @ <h1>MLINK table for check-in %h(zCI)</h1> |
| 933 | render_checkin_context(mid, 0, 1, 0); |
| 934 | style_table_sorter(); |
| 935 | @ <hr /> |
| 936 | @ <div class='brlist'> |
| 937 | @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'> |
| 938 | @ <thead><tr> |
| 939 | @ <th>File</th> |
| 940 | @ <th>Parent<br>Check-in</th> |
| 941 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -767,11 +767,11 @@ | |
| 767 | @ </span></span> |
| 768 | } |
| 769 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 770 | int ii; |
| 771 | char *zAncLink; |
| 772 | @ <br>fid=%d(frid) \ |
| 773 | @ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \ |
| 774 | @ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \ |
| 775 | @ pfnid=%d(pfnid) mxfnid=%d(mxfnid) |
| 776 | if( nParent>0 ){ |
| 777 | @ parents=%lld(aParent[0]) |
| @@ -930,11 +930,11 @@ | |
| 930 | mid |
| 931 | ); |
| 932 | @ <h1>MLINK table for check-in %h(zCI)</h1> |
| 933 | render_checkin_context(mid, 0, 1, 0); |
| 934 | style_table_sorter(); |
| 935 | @ <hr> |
| 936 | @ <div class='brlist'> |
| 937 | @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'> |
| 938 | @ <thead><tr> |
| 939 | @ <th>File</th> |
| 940 | @ <th>Parent<br>Check-in</th> |
| 941 |
+106
-29
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -563,10 +563,63 @@ | ||
| 563 | 563 | } |
| 564 | 564 | fossil_print("\n"); |
| 565 | 565 | } |
| 566 | 566 | forumthread_delete(pThread); |
| 567 | 567 | } |
| 568 | + | |
| 569 | +/* | |
| 570 | +** WEBPAGE: forumthreadhashlist | |
| 571 | +** | |
| 572 | +** Usage: /forumthreadhashlist/HASH-OF-ROOT | |
| 573 | +** | |
| 574 | +** This page (accessibly only to admins) shows a list of all artifacts | |
| 575 | +** associated with a single forum thread. An admin might copy/paste this | |
| 576 | +** list into the /shun page in order to shun an entire thread. | |
| 577 | +*/ | |
| 578 | +void forumthreadhashlist(void){ | |
| 579 | + int fpid; | |
| 580 | + int froot; | |
| 581 | + const char *zName = P("name"); | |
| 582 | + ForumThread *pThread; | |
| 583 | + ForumPost *p; | |
| 584 | + char *fuuid; | |
| 585 | + | |
| 586 | + login_check_credentials(); | |
| 587 | + if( !g.perm.Admin ){ | |
| 588 | + return; | |
| 589 | + } | |
| 590 | + if( zName==0 ){ | |
| 591 | + webpage_error("Missing \"name=\" query parameter"); | |
| 592 | + } | |
| 593 | + fpid = symbolic_name_to_rid(zName, "f"); | |
| 594 | + if( fpid<=0 ){ | |
| 595 | + if( fpid==0 ){ | |
| 596 | + webpage_notfound_error("Unknown forum id: \"%s\"", zName); | |
| 597 | + }else{ | |
| 598 | + ambiguous_page(); | |
| 599 | + } | |
| 600 | + return; | |
| 601 | + } | |
| 602 | + froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); | |
| 603 | + if( froot==0 ){ | |
| 604 | + webpage_notfound_error("Not a forum post: \"%s\"", zName); | |
| 605 | + } | |
| 606 | + fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot); | |
| 607 | + style_set_current_feature("forum"); | |
| 608 | + style_header("Artifacts Of Forum Thread"); | |
| 609 | + @ <h2> | |
| 610 | + @ Artifacts associated with the forum thread | |
| 611 | + @ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2> | |
| 612 | + @ <pre> | |
| 613 | + pThread = forumthread_create(froot, 1); | |
| 614 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 615 | + @ %h(p->zUuid) | |
| 616 | + } | |
| 617 | + forumthread_delete(pThread); | |
| 618 | + @ </pre> | |
| 619 | + style_finish_page(); | |
| 620 | +} | |
| 568 | 621 | |
| 569 | 622 | /* |
| 570 | 623 | ** Render a forum post for display |
| 571 | 624 | */ |
| 572 | 625 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | ||
| 1178 | 1231 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1179 | 1232 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1180 | 1233 | } |
| 1181 | 1234 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1182 | 1235 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1236 | + if( g.perm.Admin ){ | |
| 1237 | + style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName); | |
| 1238 | + } | |
| 1183 | 1239 | |
| 1184 | 1240 | /* Display the thread. */ |
| 1185 | 1241 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1186 | 1242 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1187 | 1243 | |
| @@ -1209,10 +1265,25 @@ | ||
| 1209 | 1265 | static int whitespace_only(const char *z){ |
| 1210 | 1266 | if( z==0 ) return 1; |
| 1211 | 1267 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1212 | 1268 | return z[0]==0; |
| 1213 | 1269 | } |
| 1270 | + | |
| 1271 | +/* Flags for use with forum_post() */ | |
| 1272 | +#define FPOST_NO_ALERT 1 /* do not send any alerts */ | |
| 1273 | + | |
| 1274 | +/* | |
| 1275 | +** Return a flags value for use with the final argument to | |
| 1276 | +** forum_post(), extracted from the CGI environment. | |
| 1277 | +*/ | |
| 1278 | +static int forum_post_flags(void){ | |
| 1279 | + int iPostFlags = 0; | |
| 1280 | + if( g.perm.Debug && P("fpsilent")!=0 ){ | |
| 1281 | + iPostFlags |= FPOST_NO_ALERT; | |
| 1282 | + } | |
| 1283 | + return iPostFlags; | |
| 1284 | +} | |
| 1214 | 1285 | |
| 1215 | 1286 | /* |
| 1216 | 1287 | ** Add a new Forum Post artifact to the repository. |
| 1217 | 1288 | ** |
| 1218 | 1289 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | ||
| 1221 | 1292 | const char *zTitle, /* Title. NULL for replies */ |
| 1222 | 1293 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1223 | 1294 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1224 | 1295 | const char *zUser, /* Username. NULL means use login name */ |
| 1225 | 1296 | const char *zMimetype, /* Mimetype of content. */ |
| 1226 | - const char *zContent /* Content */ | |
| 1297 | + const char *zContent, /* Content */ | |
| 1298 | + int iFlags /* FPOST_xyz flag values */ | |
| 1227 | 1299 | ){ |
| 1228 | 1300 | char *zDate; |
| 1229 | 1301 | char *zI; |
| 1230 | 1302 | char *zG; |
| 1231 | 1303 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | ||
| 1308 | 1380 | @ <pre>%h(blob_str(&x))</pre> |
| 1309 | 1381 | @ </div> |
| 1310 | 1382 | blob_reset(&x); |
| 1311 | 1383 | return 0; |
| 1312 | 1384 | }else{ |
| 1313 | - int nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, | |
| 1314 | - forum_need_moderation()); | |
| 1385 | + int nrid; | |
| 1386 | + db_begin_transaction(); | |
| 1387 | + nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); | |
| 1315 | 1388 | blob_reset(&x); |
| 1389 | + if( (iFlags & FPOST_NO_ALERT)!=0 ){ | |
| 1390 | + alert_unqueue('f', nrid); | |
| 1391 | + } | |
| 1392 | + db_commit_transaction(); | |
| 1316 | 1393 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1317 | 1394 | return 1; |
| 1318 | 1395 | } |
| 1319 | 1396 | } |
| 1320 | 1397 | |
| @@ -1439,10 +1516,27 @@ | ||
| 1439 | 1516 | @ From: anonymous<br> |
| 1440 | 1517 | }else{ |
| 1441 | 1518 | @ From: %h(login_name())<br> |
| 1442 | 1519 | } |
| 1443 | 1520 | } |
| 1521 | + | |
| 1522 | +static void forum_render_debug_options(void){ | |
| 1523 | + if( g.perm.Debug ){ | |
| 1524 | + /* Give extra control over the post to users with the special | |
| 1525 | + * Debug capability, which includes Admin and Setup users */ | |
| 1526 | + @ <div class="debug"> | |
| 1527 | + @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1528 | + @ Dry run</label> | |
| 1529 | + @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1530 | + @ Require moderator approval</label> | |
| 1531 | + @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1532 | + @ Show query parameters</label> | |
| 1533 | + @ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \ | |
| 1534 | + @ Do not sent notification emails</label> | |
| 1535 | + @ </div> | |
| 1536 | + } | |
| 1537 | +} | |
| 1444 | 1538 | |
| 1445 | 1539 | /* |
| 1446 | 1540 | ** WEBPAGE: forume1 |
| 1447 | 1541 | ** |
| 1448 | 1542 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | ||
| 1449 | 1543 | */ |
| 1450 | 1544 | void forumnew_page(void){ |
| 1451 | 1545 | const char *zTitle = PDT("title",""); |
| 1452 | 1546 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1453 | 1547 | const char *zContent = PDT("content",""); |
| 1548 | + | |
| 1454 | 1549 | login_check_credentials(); |
| 1455 | 1550 | if( !g.perm.WrForum ){ |
| 1456 | 1551 | login_needed(g.anon.WrForum); |
| 1457 | 1552 | return; |
| 1458 | 1553 | } |
| 1459 | 1554 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1460 | - if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; | |
| 1555 | + if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, | |
| 1556 | + forum_post_flags()) ) return; | |
| 1461 | 1557 | } |
| 1462 | 1558 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1463 | 1559 | @ <h1>Preview:</h1> |
| 1464 | 1560 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1465 | 1561 | } |
| @@ -1473,22 +1569,11 @@ | ||
| 1473 | 1569 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1474 | 1570 | @ <input type="submit" name="submit" value="Submit"> |
| 1475 | 1571 | }else{ |
| 1476 | 1572 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1477 | 1573 | } |
| 1478 | - if( g.perm.Debug ){ | |
| 1479 | - /* Give extra control over the post to users with the special | |
| 1480 | - * Debug capability, which includes Admin and Setup users */ | |
| 1481 | - @ <div class="debug"> | |
| 1482 | - @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1483 | - @ Dry run</label> | |
| 1484 | - @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1485 | - @ Require moderator approval</label> | |
| 1486 | - @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1487 | - @ Show query parameters</label> | |
| 1488 | - @ </div> | |
| 1489 | - } | |
| 1574 | + forum_render_debug_options(); | |
| 1490 | 1575 | @ </form> |
| 1491 | 1576 | forum_emit_js(); |
| 1492 | 1577 | style_finish_page(); |
| 1493 | 1578 | } |
| 1494 | 1579 | |
| @@ -1581,13 +1666,15 @@ | ||
| 1581 | 1666 | && (!whitespace_only(zContent) || isDelete) |
| 1582 | 1667 | ){ |
| 1583 | 1668 | int done = 1; |
| 1584 | 1669 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1585 | 1670 | if( P("reply") ){ |
| 1586 | - done = forum_post(0, fpid, 0, 0, zMimetype, zContent); | |
| 1671 | + done = forum_post(0, fpid, 0, 0, zMimetype, zContent, | |
| 1672 | + forum_post_flags()); | |
| 1587 | 1673 | }else if( P("edit") || isDelete ){ |
| 1588 | - done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); | |
| 1674 | + done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent, | |
| 1675 | + forum_post_flags()); | |
| 1589 | 1676 | }else{ |
| 1590 | 1677 | webpage_error("Missing 'reply' query parameter"); |
| 1591 | 1678 | } |
| 1592 | 1679 | if( done ) return; |
| 1593 | 1680 | } |
| @@ -1671,21 +1758,11 @@ | ||
| 1671 | 1758 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1672 | 1759 | if( !iClosed || g.perm.Admin ) { |
| 1673 | 1760 | @ <input type="submit" name="submit" value="Submit"> |
| 1674 | 1761 | } |
| 1675 | 1762 | } |
| 1676 | - if( g.perm.Debug ){ | |
| 1677 | - /* For the test-forumnew page add these extra debugging controls */ | |
| 1678 | - @ <div class="debug"> | |
| 1679 | - @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1680 | - @ Dry run</label> | |
| 1681 | - @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1682 | - @ Require moderator approval</label> | |
| 1683 | - @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1684 | - @ Show query parameters</label> | |
| 1685 | - @ </div> | |
| 1686 | - } | |
| 1763 | + forum_render_debug_options(); | |
| 1687 | 1764 | @ </form> |
| 1688 | 1765 | forum_emit_js(); |
| 1689 | 1766 | forumpost_emit_closed_state(fpid, iClosed); |
| 1690 | 1767 | style_finish_page(); |
| 1691 | 1768 | } |
| 1692 | 1769 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -563,10 +563,63 @@ | |
| 563 | } |
| 564 | fossil_print("\n"); |
| 565 | } |
| 566 | forumthread_delete(pThread); |
| 567 | } |
| 568 | |
| 569 | /* |
| 570 | ** Render a forum post for display |
| 571 | */ |
| 572 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | |
| 1178 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1179 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1180 | } |
| 1181 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1182 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1183 | |
| 1184 | /* Display the thread. */ |
| 1185 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1186 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1187 | |
| @@ -1209,10 +1265,25 @@ | |
| 1209 | static int whitespace_only(const char *z){ |
| 1210 | if( z==0 ) return 1; |
| 1211 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1212 | return z[0]==0; |
| 1213 | } |
| 1214 | |
| 1215 | /* |
| 1216 | ** Add a new Forum Post artifact to the repository. |
| 1217 | ** |
| 1218 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | |
| 1221 | const char *zTitle, /* Title. NULL for replies */ |
| 1222 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1223 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1224 | const char *zUser, /* Username. NULL means use login name */ |
| 1225 | const char *zMimetype, /* Mimetype of content. */ |
| 1226 | const char *zContent /* Content */ |
| 1227 | ){ |
| 1228 | char *zDate; |
| 1229 | char *zI; |
| 1230 | char *zG; |
| 1231 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | |
| 1308 | @ <pre>%h(blob_str(&x))</pre> |
| 1309 | @ </div> |
| 1310 | blob_reset(&x); |
| 1311 | return 0; |
| 1312 | }else{ |
| 1313 | int nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, |
| 1314 | forum_need_moderation()); |
| 1315 | blob_reset(&x); |
| 1316 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1317 | return 1; |
| 1318 | } |
| 1319 | } |
| 1320 | |
| @@ -1439,10 +1516,27 @@ | |
| 1439 | @ From: anonymous<br> |
| 1440 | }else{ |
| 1441 | @ From: %h(login_name())<br> |
| 1442 | } |
| 1443 | } |
| 1444 | |
| 1445 | /* |
| 1446 | ** WEBPAGE: forume1 |
| 1447 | ** |
| 1448 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | |
| 1449 | */ |
| 1450 | void forumnew_page(void){ |
| 1451 | const char *zTitle = PDT("title",""); |
| 1452 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1453 | const char *zContent = PDT("content",""); |
| 1454 | login_check_credentials(); |
| 1455 | if( !g.perm.WrForum ){ |
| 1456 | login_needed(g.anon.WrForum); |
| 1457 | return; |
| 1458 | } |
| 1459 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1460 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; |
| 1461 | } |
| 1462 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1463 | @ <h1>Preview:</h1> |
| 1464 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1465 | } |
| @@ -1473,22 +1569,11 @@ | |
| 1473 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1474 | @ <input type="submit" name="submit" value="Submit"> |
| 1475 | }else{ |
| 1476 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1477 | } |
| 1478 | if( g.perm.Debug ){ |
| 1479 | /* Give extra control over the post to users with the special |
| 1480 | * Debug capability, which includes Admin and Setup users */ |
| 1481 | @ <div class="debug"> |
| 1482 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1483 | @ Dry run</label> |
| 1484 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1485 | @ Require moderator approval</label> |
| 1486 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1487 | @ Show query parameters</label> |
| 1488 | @ </div> |
| 1489 | } |
| 1490 | @ </form> |
| 1491 | forum_emit_js(); |
| 1492 | style_finish_page(); |
| 1493 | } |
| 1494 | |
| @@ -1581,13 +1666,15 @@ | |
| 1581 | && (!whitespace_only(zContent) || isDelete) |
| 1582 | ){ |
| 1583 | int done = 1; |
| 1584 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1585 | if( P("reply") ){ |
| 1586 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent); |
| 1587 | }else if( P("edit") || isDelete ){ |
| 1588 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); |
| 1589 | }else{ |
| 1590 | webpage_error("Missing 'reply' query parameter"); |
| 1591 | } |
| 1592 | if( done ) return; |
| 1593 | } |
| @@ -1671,21 +1758,11 @@ | |
| 1671 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1672 | if( !iClosed || g.perm.Admin ) { |
| 1673 | @ <input type="submit" name="submit" value="Submit"> |
| 1674 | } |
| 1675 | } |
| 1676 | if( g.perm.Debug ){ |
| 1677 | /* For the test-forumnew page add these extra debugging controls */ |
| 1678 | @ <div class="debug"> |
| 1679 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1680 | @ Dry run</label> |
| 1681 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1682 | @ Require moderator approval</label> |
| 1683 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1684 | @ Show query parameters</label> |
| 1685 | @ </div> |
| 1686 | } |
| 1687 | @ </form> |
| 1688 | forum_emit_js(); |
| 1689 | forumpost_emit_closed_state(fpid, iClosed); |
| 1690 | style_finish_page(); |
| 1691 | } |
| 1692 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -563,10 +563,63 @@ | |
| 563 | } |
| 564 | fossil_print("\n"); |
| 565 | } |
| 566 | forumthread_delete(pThread); |
| 567 | } |
| 568 | |
| 569 | /* |
| 570 | ** WEBPAGE: forumthreadhashlist |
| 571 | ** |
| 572 | ** Usage: /forumthreadhashlist/HASH-OF-ROOT |
| 573 | ** |
| 574 | ** This page (accessibly only to admins) shows a list of all artifacts |
| 575 | ** associated with a single forum thread. An admin might copy/paste this |
| 576 | ** list into the /shun page in order to shun an entire thread. |
| 577 | */ |
| 578 | void forumthreadhashlist(void){ |
| 579 | int fpid; |
| 580 | int froot; |
| 581 | const char *zName = P("name"); |
| 582 | ForumThread *pThread; |
| 583 | ForumPost *p; |
| 584 | char *fuuid; |
| 585 | |
| 586 | login_check_credentials(); |
| 587 | if( !g.perm.Admin ){ |
| 588 | return; |
| 589 | } |
| 590 | if( zName==0 ){ |
| 591 | webpage_error("Missing \"name=\" query parameter"); |
| 592 | } |
| 593 | fpid = symbolic_name_to_rid(zName, "f"); |
| 594 | if( fpid<=0 ){ |
| 595 | if( fpid==0 ){ |
| 596 | webpage_notfound_error("Unknown forum id: \"%s\"", zName); |
| 597 | }else{ |
| 598 | ambiguous_page(); |
| 599 | } |
| 600 | return; |
| 601 | } |
| 602 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 603 | if( froot==0 ){ |
| 604 | webpage_notfound_error("Not a forum post: \"%s\"", zName); |
| 605 | } |
| 606 | fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot); |
| 607 | style_set_current_feature("forum"); |
| 608 | style_header("Artifacts Of Forum Thread"); |
| 609 | @ <h2> |
| 610 | @ Artifacts associated with the forum thread |
| 611 | @ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2> |
| 612 | @ <pre> |
| 613 | pThread = forumthread_create(froot, 1); |
| 614 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 615 | @ %h(p->zUuid) |
| 616 | } |
| 617 | forumthread_delete(pThread); |
| 618 | @ </pre> |
| 619 | style_finish_page(); |
| 620 | } |
| 621 | |
| 622 | /* |
| 623 | ** Render a forum post for display |
| 624 | */ |
| 625 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | |
| 1231 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1232 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1233 | } |
| 1234 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1235 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1236 | if( g.perm.Admin ){ |
| 1237 | style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName); |
| 1238 | } |
| 1239 | |
| 1240 | /* Display the thread. */ |
| 1241 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1242 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1243 | |
| @@ -1209,10 +1265,25 @@ | |
| 1265 | static int whitespace_only(const char *z){ |
| 1266 | if( z==0 ) return 1; |
| 1267 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1268 | return z[0]==0; |
| 1269 | } |
| 1270 | |
| 1271 | /* Flags for use with forum_post() */ |
| 1272 | #define FPOST_NO_ALERT 1 /* do not send any alerts */ |
| 1273 | |
| 1274 | /* |
| 1275 | ** Return a flags value for use with the final argument to |
| 1276 | ** forum_post(), extracted from the CGI environment. |
| 1277 | */ |
| 1278 | static int forum_post_flags(void){ |
| 1279 | int iPostFlags = 0; |
| 1280 | if( g.perm.Debug && P("fpsilent")!=0 ){ |
| 1281 | iPostFlags |= FPOST_NO_ALERT; |
| 1282 | } |
| 1283 | return iPostFlags; |
| 1284 | } |
| 1285 | |
| 1286 | /* |
| 1287 | ** Add a new Forum Post artifact to the repository. |
| 1288 | ** |
| 1289 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | |
| 1292 | const char *zTitle, /* Title. NULL for replies */ |
| 1293 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1294 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1295 | const char *zUser, /* Username. NULL means use login name */ |
| 1296 | const char *zMimetype, /* Mimetype of content. */ |
| 1297 | const char *zContent, /* Content */ |
| 1298 | int iFlags /* FPOST_xyz flag values */ |
| 1299 | ){ |
| 1300 | char *zDate; |
| 1301 | char *zI; |
| 1302 | char *zG; |
| 1303 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | |
| 1380 | @ <pre>%h(blob_str(&x))</pre> |
| 1381 | @ </div> |
| 1382 | blob_reset(&x); |
| 1383 | return 0; |
| 1384 | }else{ |
| 1385 | int nrid; |
| 1386 | db_begin_transaction(); |
| 1387 | nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); |
| 1388 | blob_reset(&x); |
| 1389 | if( (iFlags & FPOST_NO_ALERT)!=0 ){ |
| 1390 | alert_unqueue('f', nrid); |
| 1391 | } |
| 1392 | db_commit_transaction(); |
| 1393 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1394 | return 1; |
| 1395 | } |
| 1396 | } |
| 1397 | |
| @@ -1439,10 +1516,27 @@ | |
| 1516 | @ From: anonymous<br> |
| 1517 | }else{ |
| 1518 | @ From: %h(login_name())<br> |
| 1519 | } |
| 1520 | } |
| 1521 | |
| 1522 | static void forum_render_debug_options(void){ |
| 1523 | if( g.perm.Debug ){ |
| 1524 | /* Give extra control over the post to users with the special |
| 1525 | * Debug capability, which includes Admin and Setup users */ |
| 1526 | @ <div class="debug"> |
| 1527 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1528 | @ Dry run</label> |
| 1529 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1530 | @ Require moderator approval</label> |
| 1531 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1532 | @ Show query parameters</label> |
| 1533 | @ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \ |
| 1534 | @ Do not sent notification emails</label> |
| 1535 | @ </div> |
| 1536 | } |
| 1537 | } |
| 1538 | |
| 1539 | /* |
| 1540 | ** WEBPAGE: forume1 |
| 1541 | ** |
| 1542 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | |
| 1543 | */ |
| 1544 | void forumnew_page(void){ |
| 1545 | const char *zTitle = PDT("title",""); |
| 1546 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1547 | const char *zContent = PDT("content",""); |
| 1548 | |
| 1549 | login_check_credentials(); |
| 1550 | if( !g.perm.WrForum ){ |
| 1551 | login_needed(g.anon.WrForum); |
| 1552 | return; |
| 1553 | } |
| 1554 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1555 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, |
| 1556 | forum_post_flags()) ) return; |
| 1557 | } |
| 1558 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1559 | @ <h1>Preview:</h1> |
| 1560 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1561 | } |
| @@ -1473,22 +1569,11 @@ | |
| 1569 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1570 | @ <input type="submit" name="submit" value="Submit"> |
| 1571 | }else{ |
| 1572 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1573 | } |
| 1574 | forum_render_debug_options(); |
| 1575 | @ </form> |
| 1576 | forum_emit_js(); |
| 1577 | style_finish_page(); |
| 1578 | } |
| 1579 | |
| @@ -1581,13 +1666,15 @@ | |
| 1666 | && (!whitespace_only(zContent) || isDelete) |
| 1667 | ){ |
| 1668 | int done = 1; |
| 1669 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1670 | if( P("reply") ){ |
| 1671 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent, |
| 1672 | forum_post_flags()); |
| 1673 | }else if( P("edit") || isDelete ){ |
| 1674 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent, |
| 1675 | forum_post_flags()); |
| 1676 | }else{ |
| 1677 | webpage_error("Missing 'reply' query parameter"); |
| 1678 | } |
| 1679 | if( done ) return; |
| 1680 | } |
| @@ -1671,21 +1758,11 @@ | |
| 1758 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1759 | if( !iClosed || g.perm.Admin ) { |
| 1760 | @ <input type="submit" name="submit" value="Submit"> |
| 1761 | } |
| 1762 | } |
| 1763 | forum_render_debug_options(); |
| 1764 | @ </form> |
| 1765 | forum_emit_js(); |
| 1766 | forumpost_emit_closed_state(fpid, iClosed); |
| 1767 | style_finish_page(); |
| 1768 | } |
| 1769 |
+106
-29
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -563,10 +563,63 @@ | ||
| 563 | 563 | } |
| 564 | 564 | fossil_print("\n"); |
| 565 | 565 | } |
| 566 | 566 | forumthread_delete(pThread); |
| 567 | 567 | } |
| 568 | + | |
| 569 | +/* | |
| 570 | +** WEBPAGE: forumthreadhashlist | |
| 571 | +** | |
| 572 | +** Usage: /forumthreadhashlist/HASH-OF-ROOT | |
| 573 | +** | |
| 574 | +** This page (accessibly only to admins) shows a list of all artifacts | |
| 575 | +** associated with a single forum thread. An admin might copy/paste this | |
| 576 | +** list into the /shun page in order to shun an entire thread. | |
| 577 | +*/ | |
| 578 | +void forumthreadhashlist(void){ | |
| 579 | + int fpid; | |
| 580 | + int froot; | |
| 581 | + const char *zName = P("name"); | |
| 582 | + ForumThread *pThread; | |
| 583 | + ForumPost *p; | |
| 584 | + char *fuuid; | |
| 585 | + | |
| 586 | + login_check_credentials(); | |
| 587 | + if( !g.perm.Admin ){ | |
| 588 | + return; | |
| 589 | + } | |
| 590 | + if( zName==0 ){ | |
| 591 | + webpage_error("Missing \"name=\" query parameter"); | |
| 592 | + } | |
| 593 | + fpid = symbolic_name_to_rid(zName, "f"); | |
| 594 | + if( fpid<=0 ){ | |
| 595 | + if( fpid==0 ){ | |
| 596 | + webpage_notfound_error("Unknown forum id: \"%s\"", zName); | |
| 597 | + }else{ | |
| 598 | + ambiguous_page(); | |
| 599 | + } | |
| 600 | + return; | |
| 601 | + } | |
| 602 | + froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); | |
| 603 | + if( froot==0 ){ | |
| 604 | + webpage_notfound_error("Not a forum post: \"%s\"", zName); | |
| 605 | + } | |
| 606 | + fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot); | |
| 607 | + style_set_current_feature("forum"); | |
| 608 | + style_header("Artifacts Of Forum Thread"); | |
| 609 | + @ <h2> | |
| 610 | + @ Artifacts associated with the forum thread | |
| 611 | + @ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2> | |
| 612 | + @ <pre> | |
| 613 | + pThread = forumthread_create(froot, 1); | |
| 614 | + for(p=pThread->pFirst; p; p=p->pNext){ | |
| 615 | + @ %h(p->zUuid) | |
| 616 | + } | |
| 617 | + forumthread_delete(pThread); | |
| 618 | + @ </pre> | |
| 619 | + style_finish_page(); | |
| 620 | +} | |
| 568 | 621 | |
| 569 | 622 | /* |
| 570 | 623 | ** Render a forum post for display |
| 571 | 624 | */ |
| 572 | 625 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | ||
| 1178 | 1231 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1179 | 1232 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1180 | 1233 | } |
| 1181 | 1234 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1182 | 1235 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1236 | + if( g.perm.Admin ){ | |
| 1237 | + style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName); | |
| 1238 | + } | |
| 1183 | 1239 | |
| 1184 | 1240 | /* Display the thread. */ |
| 1185 | 1241 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1186 | 1242 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1187 | 1243 | |
| @@ -1209,10 +1265,25 @@ | ||
| 1209 | 1265 | static int whitespace_only(const char *z){ |
| 1210 | 1266 | if( z==0 ) return 1; |
| 1211 | 1267 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1212 | 1268 | return z[0]==0; |
| 1213 | 1269 | } |
| 1270 | + | |
| 1271 | +/* Flags for use with forum_post() */ | |
| 1272 | +#define FPOST_NO_ALERT 1 /* do not send any alerts */ | |
| 1273 | + | |
| 1274 | +/* | |
| 1275 | +** Return a flags value for use with the final argument to | |
| 1276 | +** forum_post(), extracted from the CGI environment. | |
| 1277 | +*/ | |
| 1278 | +static int forum_post_flags(void){ | |
| 1279 | + int iPostFlags = 0; | |
| 1280 | + if( g.perm.Debug && P("fpsilent")!=0 ){ | |
| 1281 | + iPostFlags |= FPOST_NO_ALERT; | |
| 1282 | + } | |
| 1283 | + return iPostFlags; | |
| 1284 | +} | |
| 1214 | 1285 | |
| 1215 | 1286 | /* |
| 1216 | 1287 | ** Add a new Forum Post artifact to the repository. |
| 1217 | 1288 | ** |
| 1218 | 1289 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | ||
| 1221 | 1292 | const char *zTitle, /* Title. NULL for replies */ |
| 1222 | 1293 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1223 | 1294 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1224 | 1295 | const char *zUser, /* Username. NULL means use login name */ |
| 1225 | 1296 | const char *zMimetype, /* Mimetype of content. */ |
| 1226 | - const char *zContent /* Content */ | |
| 1297 | + const char *zContent, /* Content */ | |
| 1298 | + int iFlags /* FPOST_xyz flag values */ | |
| 1227 | 1299 | ){ |
| 1228 | 1300 | char *zDate; |
| 1229 | 1301 | char *zI; |
| 1230 | 1302 | char *zG; |
| 1231 | 1303 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | ||
| 1308 | 1380 | @ <pre>%h(blob_str(&x))</pre> |
| 1309 | 1381 | @ </div> |
| 1310 | 1382 | blob_reset(&x); |
| 1311 | 1383 | return 0; |
| 1312 | 1384 | }else{ |
| 1313 | - int nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, | |
| 1314 | - forum_need_moderation()); | |
| 1385 | + int nrid; | |
| 1386 | + db_begin_transaction(); | |
| 1387 | + nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); | |
| 1315 | 1388 | blob_reset(&x); |
| 1389 | + if( (iFlags & FPOST_NO_ALERT)!=0 ){ | |
| 1390 | + alert_unqueue('f', nrid); | |
| 1391 | + } | |
| 1392 | + db_commit_transaction(); | |
| 1316 | 1393 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1317 | 1394 | return 1; |
| 1318 | 1395 | } |
| 1319 | 1396 | } |
| 1320 | 1397 | |
| @@ -1439,10 +1516,27 @@ | ||
| 1439 | 1516 | @ From: anonymous<br> |
| 1440 | 1517 | }else{ |
| 1441 | 1518 | @ From: %h(login_name())<br> |
| 1442 | 1519 | } |
| 1443 | 1520 | } |
| 1521 | + | |
| 1522 | +static void forum_render_debug_options(void){ | |
| 1523 | + if( g.perm.Debug ){ | |
| 1524 | + /* Give extra control over the post to users with the special | |
| 1525 | + * Debug capability, which includes Admin and Setup users */ | |
| 1526 | + @ <div class="debug"> | |
| 1527 | + @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1528 | + @ Dry run</label> | |
| 1529 | + @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1530 | + @ Require moderator approval</label> | |
| 1531 | + @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1532 | + @ Show query parameters</label> | |
| 1533 | + @ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \ | |
| 1534 | + @ Do not sent notification emails</label> | |
| 1535 | + @ </div> | |
| 1536 | + } | |
| 1537 | +} | |
| 1444 | 1538 | |
| 1445 | 1539 | /* |
| 1446 | 1540 | ** WEBPAGE: forume1 |
| 1447 | 1541 | ** |
| 1448 | 1542 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | ||
| 1449 | 1543 | */ |
| 1450 | 1544 | void forumnew_page(void){ |
| 1451 | 1545 | const char *zTitle = PDT("title",""); |
| 1452 | 1546 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1453 | 1547 | const char *zContent = PDT("content",""); |
| 1548 | + | |
| 1454 | 1549 | login_check_credentials(); |
| 1455 | 1550 | if( !g.perm.WrForum ){ |
| 1456 | 1551 | login_needed(g.anon.WrForum); |
| 1457 | 1552 | return; |
| 1458 | 1553 | } |
| 1459 | 1554 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1460 | - if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; | |
| 1555 | + if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, | |
| 1556 | + forum_post_flags()) ) return; | |
| 1461 | 1557 | } |
| 1462 | 1558 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1463 | 1559 | @ <h1>Preview:</h1> |
| 1464 | 1560 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1465 | 1561 | } |
| @@ -1473,22 +1569,11 @@ | ||
| 1473 | 1569 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1474 | 1570 | @ <input type="submit" name="submit" value="Submit"> |
| 1475 | 1571 | }else{ |
| 1476 | 1572 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1477 | 1573 | } |
| 1478 | - if( g.perm.Debug ){ | |
| 1479 | - /* Give extra control over the post to users with the special | |
| 1480 | - * Debug capability, which includes Admin and Setup users */ | |
| 1481 | - @ <div class="debug"> | |
| 1482 | - @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1483 | - @ Dry run</label> | |
| 1484 | - @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1485 | - @ Require moderator approval</label> | |
| 1486 | - @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1487 | - @ Show query parameters</label> | |
| 1488 | - @ </div> | |
| 1489 | - } | |
| 1574 | + forum_render_debug_options(); | |
| 1490 | 1575 | @ </form> |
| 1491 | 1576 | forum_emit_js(); |
| 1492 | 1577 | style_finish_page(); |
| 1493 | 1578 | } |
| 1494 | 1579 | |
| @@ -1581,13 +1666,15 @@ | ||
| 1581 | 1666 | && (!whitespace_only(zContent) || isDelete) |
| 1582 | 1667 | ){ |
| 1583 | 1668 | int done = 1; |
| 1584 | 1669 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1585 | 1670 | if( P("reply") ){ |
| 1586 | - done = forum_post(0, fpid, 0, 0, zMimetype, zContent); | |
| 1671 | + done = forum_post(0, fpid, 0, 0, zMimetype, zContent, | |
| 1672 | + forum_post_flags()); | |
| 1587 | 1673 | }else if( P("edit") || isDelete ){ |
| 1588 | - done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); | |
| 1674 | + done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent, | |
| 1675 | + forum_post_flags()); | |
| 1589 | 1676 | }else{ |
| 1590 | 1677 | webpage_error("Missing 'reply' query parameter"); |
| 1591 | 1678 | } |
| 1592 | 1679 | if( done ) return; |
| 1593 | 1680 | } |
| @@ -1671,21 +1758,11 @@ | ||
| 1671 | 1758 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1672 | 1759 | if( !iClosed || g.perm.Admin ) { |
| 1673 | 1760 | @ <input type="submit" name="submit" value="Submit"> |
| 1674 | 1761 | } |
| 1675 | 1762 | } |
| 1676 | - if( g.perm.Debug ){ | |
| 1677 | - /* For the test-forumnew page add these extra debugging controls */ | |
| 1678 | - @ <div class="debug"> | |
| 1679 | - @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ | |
| 1680 | - @ Dry run</label> | |
| 1681 | - @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ | |
| 1682 | - @ Require moderator approval</label> | |
| 1683 | - @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ | |
| 1684 | - @ Show query parameters</label> | |
| 1685 | - @ </div> | |
| 1686 | - } | |
| 1763 | + forum_render_debug_options(); | |
| 1687 | 1764 | @ </form> |
| 1688 | 1765 | forum_emit_js(); |
| 1689 | 1766 | forumpost_emit_closed_state(fpid, iClosed); |
| 1690 | 1767 | style_finish_page(); |
| 1691 | 1768 | } |
| 1692 | 1769 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -563,10 +563,63 @@ | |
| 563 | } |
| 564 | fossil_print("\n"); |
| 565 | } |
| 566 | forumthread_delete(pThread); |
| 567 | } |
| 568 | |
| 569 | /* |
| 570 | ** Render a forum post for display |
| 571 | */ |
| 572 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | |
| 1178 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1179 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1180 | } |
| 1181 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1182 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1183 | |
| 1184 | /* Display the thread. */ |
| 1185 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1186 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1187 | |
| @@ -1209,10 +1265,25 @@ | |
| 1209 | static int whitespace_only(const char *z){ |
| 1210 | if( z==0 ) return 1; |
| 1211 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1212 | return z[0]==0; |
| 1213 | } |
| 1214 | |
| 1215 | /* |
| 1216 | ** Add a new Forum Post artifact to the repository. |
| 1217 | ** |
| 1218 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | |
| 1221 | const char *zTitle, /* Title. NULL for replies */ |
| 1222 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1223 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1224 | const char *zUser, /* Username. NULL means use login name */ |
| 1225 | const char *zMimetype, /* Mimetype of content. */ |
| 1226 | const char *zContent /* Content */ |
| 1227 | ){ |
| 1228 | char *zDate; |
| 1229 | char *zI; |
| 1230 | char *zG; |
| 1231 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | |
| 1308 | @ <pre>%h(blob_str(&x))</pre> |
| 1309 | @ </div> |
| 1310 | blob_reset(&x); |
| 1311 | return 0; |
| 1312 | }else{ |
| 1313 | int nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, |
| 1314 | forum_need_moderation()); |
| 1315 | blob_reset(&x); |
| 1316 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1317 | return 1; |
| 1318 | } |
| 1319 | } |
| 1320 | |
| @@ -1439,10 +1516,27 @@ | |
| 1439 | @ From: anonymous<br> |
| 1440 | }else{ |
| 1441 | @ From: %h(login_name())<br> |
| 1442 | } |
| 1443 | } |
| 1444 | |
| 1445 | /* |
| 1446 | ** WEBPAGE: forume1 |
| 1447 | ** |
| 1448 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | |
| 1449 | */ |
| 1450 | void forumnew_page(void){ |
| 1451 | const char *zTitle = PDT("title",""); |
| 1452 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1453 | const char *zContent = PDT("content",""); |
| 1454 | login_check_credentials(); |
| 1455 | if( !g.perm.WrForum ){ |
| 1456 | login_needed(g.anon.WrForum); |
| 1457 | return; |
| 1458 | } |
| 1459 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1460 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return; |
| 1461 | } |
| 1462 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1463 | @ <h1>Preview:</h1> |
| 1464 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1465 | } |
| @@ -1473,22 +1569,11 @@ | |
| 1473 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1474 | @ <input type="submit" name="submit" value="Submit"> |
| 1475 | }else{ |
| 1476 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1477 | } |
| 1478 | if( g.perm.Debug ){ |
| 1479 | /* Give extra control over the post to users with the special |
| 1480 | * Debug capability, which includes Admin and Setup users */ |
| 1481 | @ <div class="debug"> |
| 1482 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1483 | @ Dry run</label> |
| 1484 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1485 | @ Require moderator approval</label> |
| 1486 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1487 | @ Show query parameters</label> |
| 1488 | @ </div> |
| 1489 | } |
| 1490 | @ </form> |
| 1491 | forum_emit_js(); |
| 1492 | style_finish_page(); |
| 1493 | } |
| 1494 | |
| @@ -1581,13 +1666,15 @@ | |
| 1581 | && (!whitespace_only(zContent) || isDelete) |
| 1582 | ){ |
| 1583 | int done = 1; |
| 1584 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1585 | if( P("reply") ){ |
| 1586 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent); |
| 1587 | }else if( P("edit") || isDelete ){ |
| 1588 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent); |
| 1589 | }else{ |
| 1590 | webpage_error("Missing 'reply' query parameter"); |
| 1591 | } |
| 1592 | if( done ) return; |
| 1593 | } |
| @@ -1671,21 +1758,11 @@ | |
| 1671 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1672 | if( !iClosed || g.perm.Admin ) { |
| 1673 | @ <input type="submit" name="submit" value="Submit"> |
| 1674 | } |
| 1675 | } |
| 1676 | if( g.perm.Debug ){ |
| 1677 | /* For the test-forumnew page add these extra debugging controls */ |
| 1678 | @ <div class="debug"> |
| 1679 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1680 | @ Dry run</label> |
| 1681 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1682 | @ Require moderator approval</label> |
| 1683 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1684 | @ Show query parameters</label> |
| 1685 | @ </div> |
| 1686 | } |
| 1687 | @ </form> |
| 1688 | forum_emit_js(); |
| 1689 | forumpost_emit_closed_state(fpid, iClosed); |
| 1690 | style_finish_page(); |
| 1691 | } |
| 1692 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -563,10 +563,63 @@ | |
| 563 | } |
| 564 | fossil_print("\n"); |
| 565 | } |
| 566 | forumthread_delete(pThread); |
| 567 | } |
| 568 | |
| 569 | /* |
| 570 | ** WEBPAGE: forumthreadhashlist |
| 571 | ** |
| 572 | ** Usage: /forumthreadhashlist/HASH-OF-ROOT |
| 573 | ** |
| 574 | ** This page (accessibly only to admins) shows a list of all artifacts |
| 575 | ** associated with a single forum thread. An admin might copy/paste this |
| 576 | ** list into the /shun page in order to shun an entire thread. |
| 577 | */ |
| 578 | void forumthreadhashlist(void){ |
| 579 | int fpid; |
| 580 | int froot; |
| 581 | const char *zName = P("name"); |
| 582 | ForumThread *pThread; |
| 583 | ForumPost *p; |
| 584 | char *fuuid; |
| 585 | |
| 586 | login_check_credentials(); |
| 587 | if( !g.perm.Admin ){ |
| 588 | return; |
| 589 | } |
| 590 | if( zName==0 ){ |
| 591 | webpage_error("Missing \"name=\" query parameter"); |
| 592 | } |
| 593 | fpid = symbolic_name_to_rid(zName, "f"); |
| 594 | if( fpid<=0 ){ |
| 595 | if( fpid==0 ){ |
| 596 | webpage_notfound_error("Unknown forum id: \"%s\"", zName); |
| 597 | }else{ |
| 598 | ambiguous_page(); |
| 599 | } |
| 600 | return; |
| 601 | } |
| 602 | froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid); |
| 603 | if( froot==0 ){ |
| 604 | webpage_notfound_error("Not a forum post: \"%s\"", zName); |
| 605 | } |
| 606 | fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot); |
| 607 | style_set_current_feature("forum"); |
| 608 | style_header("Artifacts Of Forum Thread"); |
| 609 | @ <h2> |
| 610 | @ Artifacts associated with the forum thread |
| 611 | @ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2> |
| 612 | @ <pre> |
| 613 | pThread = forumthread_create(froot, 1); |
| 614 | for(p=pThread->pFirst; p; p=p->pNext){ |
| 615 | @ %h(p->zUuid) |
| 616 | } |
| 617 | forumthread_delete(pThread); |
| 618 | @ </pre> |
| 619 | style_finish_page(); |
| 620 | } |
| 621 | |
| 622 | /* |
| 623 | ** Render a forum post for display |
| 624 | */ |
| 625 | void forum_render( |
| @@ -1178,10 +1231,13 @@ | |
| 1231 | style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName, |
| 1232 | bUnf ? "&unf" : "", bHist ? "&hist" : ""); |
| 1233 | } |
| 1234 | style_submenu_checkbox("unf", "Unformatted", 0, 0); |
| 1235 | style_submenu_checkbox("hist", "History", 0, 0); |
| 1236 | if( g.perm.Admin ){ |
| 1237 | style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName); |
| 1238 | } |
| 1239 | |
| 1240 | /* Display the thread. */ |
| 1241 | if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0; |
| 1242 | forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist); |
| 1243 | |
| @@ -1209,10 +1265,25 @@ | |
| 1265 | static int whitespace_only(const char *z){ |
| 1266 | if( z==0 ) return 1; |
| 1267 | while( z[0] && fossil_isspace(z[0]) ){ z++; } |
| 1268 | return z[0]==0; |
| 1269 | } |
| 1270 | |
| 1271 | /* Flags for use with forum_post() */ |
| 1272 | #define FPOST_NO_ALERT 1 /* do not send any alerts */ |
| 1273 | |
| 1274 | /* |
| 1275 | ** Return a flags value for use with the final argument to |
| 1276 | ** forum_post(), extracted from the CGI environment. |
| 1277 | */ |
| 1278 | static int forum_post_flags(void){ |
| 1279 | int iPostFlags = 0; |
| 1280 | if( g.perm.Debug && P("fpsilent")!=0 ){ |
| 1281 | iPostFlags |= FPOST_NO_ALERT; |
| 1282 | } |
| 1283 | return iPostFlags; |
| 1284 | } |
| 1285 | |
| 1286 | /* |
| 1287 | ** Add a new Forum Post artifact to the repository. |
| 1288 | ** |
| 1289 | ** Return true if a redirect occurs. |
| @@ -1221,11 +1292,12 @@ | |
| 1292 | const char *zTitle, /* Title. NULL for replies */ |
| 1293 | int iInReplyTo, /* Post replying to. 0 for new threads */ |
| 1294 | int iEdit, /* Post being edited, or zero for a new post */ |
| 1295 | const char *zUser, /* Username. NULL means use login name */ |
| 1296 | const char *zMimetype, /* Mimetype of content. */ |
| 1297 | const char *zContent, /* Content */ |
| 1298 | int iFlags /* FPOST_xyz flag values */ |
| 1299 | ){ |
| 1300 | char *zDate; |
| 1301 | char *zI; |
| 1302 | char *zG; |
| 1303 | int iBasis; |
| @@ -1308,13 +1380,18 @@ | |
| 1380 | @ <pre>%h(blob_str(&x))</pre> |
| 1381 | @ </div> |
| 1382 | blob_reset(&x); |
| 1383 | return 0; |
| 1384 | }else{ |
| 1385 | int nrid; |
| 1386 | db_begin_transaction(); |
| 1387 | nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation()); |
| 1388 | blob_reset(&x); |
| 1389 | if( (iFlags & FPOST_NO_ALERT)!=0 ){ |
| 1390 | alert_unqueue('f', nrid); |
| 1391 | } |
| 1392 | db_commit_transaction(); |
| 1393 | cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid)); |
| 1394 | return 1; |
| 1395 | } |
| 1396 | } |
| 1397 | |
| @@ -1439,10 +1516,27 @@ | |
| 1516 | @ From: anonymous<br> |
| 1517 | }else{ |
| 1518 | @ From: %h(login_name())<br> |
| 1519 | } |
| 1520 | } |
| 1521 | |
| 1522 | static void forum_render_debug_options(void){ |
| 1523 | if( g.perm.Debug ){ |
| 1524 | /* Give extra control over the post to users with the special |
| 1525 | * Debug capability, which includes Admin and Setup users */ |
| 1526 | @ <div class="debug"> |
| 1527 | @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \ |
| 1528 | @ Dry run</label> |
| 1529 | @ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \ |
| 1530 | @ Require moderator approval</label> |
| 1531 | @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ |
| 1532 | @ Show query parameters</label> |
| 1533 | @ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \ |
| 1534 | @ Do not sent notification emails</label> |
| 1535 | @ </div> |
| 1536 | } |
| 1537 | } |
| 1538 | |
| 1539 | /* |
| 1540 | ** WEBPAGE: forume1 |
| 1541 | ** |
| 1542 | ** Start a new forum thread. |
| @@ -1449,17 +1543,19 @@ | |
| 1543 | */ |
| 1544 | void forumnew_page(void){ |
| 1545 | const char *zTitle = PDT("title",""); |
| 1546 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1547 | const char *zContent = PDT("content",""); |
| 1548 | |
| 1549 | login_check_credentials(); |
| 1550 | if( !g.perm.WrForum ){ |
| 1551 | login_needed(g.anon.WrForum); |
| 1552 | return; |
| 1553 | } |
| 1554 | if( P("submit") && cgi_csrf_safe(1) ){ |
| 1555 | if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent, |
| 1556 | forum_post_flags()) ) return; |
| 1557 | } |
| 1558 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1559 | @ <h1>Preview:</h1> |
| 1560 | forum_render(zTitle, zMimetype, zContent, "forumEdit", 1); |
| 1561 | } |
| @@ -1473,22 +1569,11 @@ | |
| 1569 | if( P("preview") && !whitespace_only(zContent) ){ |
| 1570 | @ <input type="submit" name="submit" value="Submit"> |
| 1571 | }else{ |
| 1572 | @ <input type="submit" name="submit" value="Submit" disabled> |
| 1573 | } |
| 1574 | forum_render_debug_options(); |
| 1575 | @ </form> |
| 1576 | forum_emit_js(); |
| 1577 | style_finish_page(); |
| 1578 | } |
| 1579 | |
| @@ -1581,13 +1666,15 @@ | |
| 1666 | && (!whitespace_only(zContent) || isDelete) |
| 1667 | ){ |
| 1668 | int done = 1; |
| 1669 | const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE); |
| 1670 | if( P("reply") ){ |
| 1671 | done = forum_post(0, fpid, 0, 0, zMimetype, zContent, |
| 1672 | forum_post_flags()); |
| 1673 | }else if( P("edit") || isDelete ){ |
| 1674 | done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent, |
| 1675 | forum_post_flags()); |
| 1676 | }else{ |
| 1677 | webpage_error("Missing 'reply' query parameter"); |
| 1678 | } |
| 1679 | if( done ) return; |
| 1680 | } |
| @@ -1671,21 +1758,11 @@ | |
| 1758 | if( (bPreview && !whitespace_only(zContent)) || isDelete ){ |
| 1759 | if( !iClosed || g.perm.Admin ) { |
| 1760 | @ <input type="submit" name="submit" value="Submit"> |
| 1761 | } |
| 1762 | } |
| 1763 | forum_render_debug_options(); |
| 1764 | @ </form> |
| 1765 | forum_emit_js(); |
| 1766 | forumpost_emit_closed_state(fpid, iClosed); |
| 1767 | style_finish_page(); |
| 1768 | } |
| 1769 |
+10
-9
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -89,17 +89,18 @@ | ||
| 89 | 89 | char **azPattern; /* Array of pointers to patterns */ |
| 90 | 90 | }; |
| 91 | 91 | #endif /* INTERFACE */ |
| 92 | 92 | |
| 93 | 93 | /* |
| 94 | -** zPatternList is a comma-separated list of glob patterns. Parse up | |
| 95 | -** that list and use it to create a new Glob object. | |
| 94 | +** zPatternList is a comma- or whitespace-separated list of glob patterns. | |
| 95 | +** Parse that list and use it to create a new Glob object. | |
| 96 | 96 | ** |
| 97 | -** Elements of the glob list may be optionally enclosed in single our | |
| 98 | -** double-quotes. This allows a comma to be part of a glob pattern. | |
| 97 | +** Elements of the glob list may be optionally enclosed in single- or | |
| 98 | +** double-quotes. This allows commas and whitespace to be part of a | |
| 99 | +** glob pattern. | |
| 99 | 100 | ** |
| 100 | -** Leading and trailing spaces on unquoted glob patterns are ignored. | |
| 101 | +** Leading and trailing spaces on glob patterns are ignored unless quoted. | |
| 101 | 102 | ** |
| 102 | 103 | ** An empty or null pattern list results in a null glob, which will |
| 103 | 104 | ** match nothing. |
| 104 | 105 | */ |
| 105 | 106 | Glob *glob_create(const char *zPatternList){ |
| @@ -126,14 +127,14 @@ | ||
| 126 | 127 | }else{ |
| 127 | 128 | delimiter = ','; |
| 128 | 129 | } |
| 129 | 130 | p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) ); |
| 130 | 131 | p->azPattern[p->nPattern++] = z; |
| 131 | - /* Find the next delimter (or the end of the string). */ | |
| 132 | - for(i=0; z[i] && z[i]!=delimiter; i++){ | |
| 133 | - if( delimiter!=',' ) continue; /* If quoted, keep going. */ | |
| 134 | - if( fossil_isspace(z[i]) ) break; /* If space, stop. */ | |
| 132 | + /* Find the next delimiter (or the end of the string). */ | |
| 133 | + for(i=0; z[i] && z[i]!=delimiter && | |
| 134 | + !(delimiter==',' && fossil_isspace(z[i])); i++){ | |
| 135 | + /* keep looking for the end of the glob pattern */ | |
| 135 | 136 | } |
| 136 | 137 | if( z[i]==0 ) break; |
| 137 | 138 | z[i] = 0; |
| 138 | 139 | z += i+1; |
| 139 | 140 | } |
| 140 | 141 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -89,17 +89,18 @@ | |
| 89 | char **azPattern; /* Array of pointers to patterns */ |
| 90 | }; |
| 91 | #endif /* INTERFACE */ |
| 92 | |
| 93 | /* |
| 94 | ** zPatternList is a comma-separated list of glob patterns. Parse up |
| 95 | ** that list and use it to create a new Glob object. |
| 96 | ** |
| 97 | ** Elements of the glob list may be optionally enclosed in single our |
| 98 | ** double-quotes. This allows a comma to be part of a glob pattern. |
| 99 | ** |
| 100 | ** Leading and trailing spaces on unquoted glob patterns are ignored. |
| 101 | ** |
| 102 | ** An empty or null pattern list results in a null glob, which will |
| 103 | ** match nothing. |
| 104 | */ |
| 105 | Glob *glob_create(const char *zPatternList){ |
| @@ -126,14 +127,14 @@ | |
| 126 | }else{ |
| 127 | delimiter = ','; |
| 128 | } |
| 129 | p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) ); |
| 130 | p->azPattern[p->nPattern++] = z; |
| 131 | /* Find the next delimter (or the end of the string). */ |
| 132 | for(i=0; z[i] && z[i]!=delimiter; i++){ |
| 133 | if( delimiter!=',' ) continue; /* If quoted, keep going. */ |
| 134 | if( fossil_isspace(z[i]) ) break; /* If space, stop. */ |
| 135 | } |
| 136 | if( z[i]==0 ) break; |
| 137 | z[i] = 0; |
| 138 | z += i+1; |
| 139 | } |
| 140 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -89,17 +89,18 @@ | |
| 89 | char **azPattern; /* Array of pointers to patterns */ |
| 90 | }; |
| 91 | #endif /* INTERFACE */ |
| 92 | |
| 93 | /* |
| 94 | ** zPatternList is a comma- or whitespace-separated list of glob patterns. |
| 95 | ** Parse that list and use it to create a new Glob object. |
| 96 | ** |
| 97 | ** Elements of the glob list may be optionally enclosed in single- or |
| 98 | ** double-quotes. This allows commas and whitespace to be part of a |
| 99 | ** glob pattern. |
| 100 | ** |
| 101 | ** Leading and trailing spaces on glob patterns are ignored unless quoted. |
| 102 | ** |
| 103 | ** An empty or null pattern list results in a null glob, which will |
| 104 | ** match nothing. |
| 105 | */ |
| 106 | Glob *glob_create(const char *zPatternList){ |
| @@ -126,14 +127,14 @@ | |
| 127 | }else{ |
| 128 | delimiter = ','; |
| 129 | } |
| 130 | p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) ); |
| 131 | p->azPattern[p->nPattern++] = z; |
| 132 | /* Find the next delimiter (or the end of the string). */ |
| 133 | for(i=0; z[i] && z[i]!=delimiter && |
| 134 | !(delimiter==',' && fossil_isspace(z[i])); i++){ |
| 135 | /* keep looking for the end of the glob pattern */ |
| 136 | } |
| 137 | if( z[i]==0 ) break; |
| 138 | z[i] = 0; |
| 139 | z += i+1; |
| 140 | } |
| 141 |
+32
-33
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1040,13 +1040,13 @@ | ||
| 1040 | 1040 | if( g.perm.ModWiki && modPending ){ |
| 1041 | 1041 | @ <div class="section">Moderation</div> |
| 1042 | 1042 | @ <blockquote> |
| 1043 | 1043 | @ <form method="POST" action="%R/winfo/%s(zUuid)"> |
| 1044 | 1044 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1045 | - @ Delete this change</label><br /> | |
| 1045 | + @ Delete this change</label><br> | |
| 1046 | 1046 | @ <label><input type="radio" name="modaction" value="approve"> |
| 1047 | - @ Approve this change</label><br /> | |
| 1047 | + @ Approve this change</label><br> | |
| 1048 | 1048 | @ <input type="submit" value="Submit"> |
| 1049 | 1049 | @ </form> |
| 1050 | 1050 | @ </blockquote> |
| 1051 | 1051 | } |
| 1052 | 1052 | |
| @@ -1296,11 +1296,11 @@ | ||
| 1296 | 1296 | @ are shown.</b></p> |
| 1297 | 1297 | } |
| 1298 | 1298 | if( zGlob ){ |
| 1299 | 1299 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1300 | 1300 | } |
| 1301 | - @<hr /><p> | |
| 1301 | + @<hr><p> | |
| 1302 | 1302 | } |
| 1303 | 1303 | blob_reset(&qp); |
| 1304 | 1304 | |
| 1305 | 1305 | manifest_file_rewind(pFrom); |
| 1306 | 1306 | pFileFrom = manifest_file_next(pFrom, 0); |
| @@ -1820,11 +1820,11 @@ | ||
| 1820 | 1820 | if( pRe ){ |
| 1821 | 1821 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1822 | 1822 | @ are shown.</b> |
| 1823 | 1823 | DCfg.pRe = pRe; |
| 1824 | 1824 | } |
| 1825 | - @ <hr /> | |
| 1825 | + @ <hr> | |
| 1826 | 1826 | append_diff(zV1, zV2, &DCfg); |
| 1827 | 1827 | append_diff_javascript(diffType); |
| 1828 | 1828 | style_finish_page(); |
| 1829 | 1829 | } |
| 1830 | 1830 | |
| @@ -2128,11 +2128,11 @@ | ||
| 2128 | 2128 | blob_zero(&downloadName); |
| 2129 | 2129 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 2130 | 2130 | object_description(rid, objdescFlags, 0, &downloadName); |
| 2131 | 2131 | style_submenu_element("Download", "%R/raw/%s?at=%T", |
| 2132 | 2132 | zUuid, file_tail(blob_str(&downloadName))); |
| 2133 | - @ <hr /> | |
| 2133 | + @ <hr> | |
| 2134 | 2134 | content_get(rid, &content); |
| 2135 | 2135 | if( !g.isHuman ){ |
| 2136 | 2136 | /* Prevent robots from running hexdump on megabyte-sized source files |
| 2137 | 2137 | ** and there by eating up lots of CPU time and bandwidth. There is |
| 2138 | 2138 | ** no good reason for a robot to need a hexdump. */ |
| @@ -2654,11 +2654,11 @@ | ||
| 2654 | 2654 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2655 | 2655 | } |
| 2656 | 2656 | if( descOnly ){ |
| 2657 | 2657 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2658 | 2658 | }else{ |
| 2659 | - @ <hr /> | |
| 2659 | + @ <hr> | |
| 2660 | 2660 | content_get(rid, &content); |
| 2661 | 2661 | if( renderAsWiki ){ |
| 2662 | 2662 | safe_html_context(DOCSRC_FILE); |
| 2663 | 2663 | wiki_render_by_mimetype(&content, zMime); |
| 2664 | 2664 | document_emit_js(); |
| @@ -2801,11 +2801,11 @@ | ||
| 2801 | 2801 | } |
| 2802 | 2802 | modPending = moderation_pending_www(rid); |
| 2803 | 2803 | @ <tr><th>Ticket:</th> |
| 2804 | 2804 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> |
| 2805 | 2805 | if( zTktTitle ){ |
| 2806 | - @<br />%h(zTktTitle) | |
| 2806 | + @<br>%h(zTktTitle) | |
| 2807 | 2807 | } |
| 2808 | 2808 | @</td></tr> |
| 2809 | 2809 | @ <tr><th>User & Date:</th><td> |
| 2810 | 2810 | hyperlink_to_user(pTktChng->zUser, zDate, " on "); |
| 2811 | 2811 | hyperlink_to_date(zDate, "</td></tr>"); |
| @@ -2816,13 +2816,13 @@ | ||
| 2816 | 2816 | if( g.perm.ModTkt && modPending ){ |
| 2817 | 2817 | @ <div class="section">Moderation</div> |
| 2818 | 2818 | @ <blockquote> |
| 2819 | 2819 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 2820 | 2820 | @ <label><input type="radio" name="modaction" value="delete"> |
| 2821 | - @ Delete this change</label><br /> | |
| 2821 | + @ Delete this change</label><br> | |
| 2822 | 2822 | @ <label><input type="radio" name="modaction" value="approve"> |
| 2823 | - @ Approve this change</label><br /> | |
| 2823 | + @ Approve this change</label><br> | |
| 2824 | 2824 | @ <input type="submit" value="Submit"> |
| 2825 | 2825 | @ </form> |
| 2826 | 2826 | @ </blockquote> |
| 2827 | 2827 | } |
| 2828 | 2828 | |
| @@ -3268,53 +3268,53 @@ | ||
| 3268 | 3268 | if( zChngTime ){ |
| 3269 | 3269 | @ <p>The timestamp on the tag used to make the changes above |
| 3270 | 3270 | @ will be overridden as: %s(date_in_standard_format(zChngTime))</p> |
| 3271 | 3271 | } |
| 3272 | 3272 | @ </blockquote> |
| 3273 | - @ <hr /> | |
| 3273 | + @ <hr> | |
| 3274 | 3274 | blob_reset(&suffix); |
| 3275 | 3275 | } |
| 3276 | 3276 | @ <p>Make changes to attributes of check-in |
| 3277 | 3277 | @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p> |
| 3278 | 3278 | form_begin(0, "%R/ci_edit"); |
| 3279 | 3279 | login_insert_csrf_secret(); |
| 3280 | - @ <div><input type="hidden" name="r" value="%s(zUuid)" /> | |
| 3280 | + @ <div><input type="hidden" name="r" value="%s(zUuid)"> | |
| 3281 | 3281 | @ <table border="0" cellspacing="10"> |
| 3282 | 3282 | |
| 3283 | 3283 | @ <tr><th align="right" valign="top">User:</th> |
| 3284 | 3284 | @ <td valign="top"> |
| 3285 | - @ <input type="text" name="u" size="20" value="%h(zNewUser)" /> | |
| 3285 | + @ <input type="text" name="u" size="20" value="%h(zNewUser)"> | |
| 3286 | 3286 | @ </td></tr> |
| 3287 | 3287 | |
| 3288 | 3288 | @ <tr><th align="right" valign="top">Comment:</th> |
| 3289 | 3289 | @ <td valign="top"> |
| 3290 | 3290 | @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea> |
| 3291 | 3291 | @ </td></tr> |
| 3292 | 3292 | |
| 3293 | 3293 | @ <tr><th align="right" valign="top">Check-in Time:</th> |
| 3294 | 3294 | @ <td valign="top"> |
| 3295 | - @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> | |
| 3295 | + @ <input type="text" name="dt" size="20" value="%h(zNewDate)"> | |
| 3296 | 3296 | @ </td></tr> |
| 3297 | 3297 | |
| 3298 | 3298 | if( zChngTime ){ |
| 3299 | 3299 | @ <tr><th align="right" valign="top">Timestamp of this change:</th> |
| 3300 | 3300 | @ <td valign="top"> |
| 3301 | - @ <input type="text" name="chngtime" size="20" value="%h(zChngTime)" /> | |
| 3301 | + @ <input type="text" name="chngtime" size="20" value="%h(zChngTime)"> | |
| 3302 | 3302 | @ </td></tr> |
| 3303 | 3303 | } |
| 3304 | 3304 | |
| 3305 | 3305 | @ <tr><th align="right" valign="top">Background Color:</th> |
| 3306 | 3306 | @ <td valign="top"> |
| 3307 | - @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) /> | |
| 3307 | + @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag)> | |
| 3308 | 3308 | @ Change background color: \ |
| 3309 | 3309 | @ <input type='color' name='clr'\ |
| 3310 | 3310 | @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div> |
| 3311 | 3311 | @ <div><label> |
| 3312 | 3312 | if( fNewPropagateColor ){ |
| 3313 | - @ <input type="checkbox" name="pclr" checked="checked" /> | |
| 3313 | + @ <input type="checkbox" name="pclr" checked="checked"> | |
| 3314 | 3314 | }else{ |
| 3315 | - @ <input type="checkbox" name="pclr" /> | |
| 3315 | + @ <input type="checkbox" name="pclr"> | |
| 3316 | 3316 | } |
| 3317 | 3317 | @ Propagate color to descendants</label></div> |
| 3318 | 3318 | @ <div class='font-size-80'>Be aware that fixed background |
| 3319 | 3319 | @ colors will not interact well with all available skins. |
| 3320 | 3320 | @ It is recommended that Fossil be allowed to select these |
| @@ -3322,14 +3322,13 @@ | ||
| 3322 | 3322 | @ preferences into account.</div> |
| 3323 | 3323 | @ </td></tr> |
| 3324 | 3324 | |
| 3325 | 3325 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3326 | 3326 | @ <td valign="top"> |
| 3327 | - @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) /> | |
| 3327 | + @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> | |
| 3328 | 3328 | @ Add the following new tag name to this check-in:</label> |
| 3329 | - @ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \ | |
| 3330 | - @ id='tagname' /> | |
| 3329 | + @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> | |
| 3331 | 3330 | zBranchName = db_text(0, "SELECT value FROM tagxref, tag" |
| 3332 | 3331 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3333 | 3332 | " AND tagxref.tagid=%d", rid, TAG_BRANCH); |
| 3334 | 3333 | db_prepare(&q, |
| 3335 | 3334 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| @@ -3353,15 +3352,15 @@ | ||
| 3353 | 3352 | }else if( !isSpecialTag && zTagName && |
| 3354 | 3353 | fossil_strcmp(&zTagName[4], zBranchName)==0){ |
| 3355 | 3354 | continue; |
| 3356 | 3355 | } |
| 3357 | 3356 | sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); |
| 3358 | - @ <br /><label> | |
| 3357 | + @ <br><label> | |
| 3359 | 3358 | if( P(zLabel) ){ |
| 3360 | - @ <input type="checkbox" name="c%d(tagid)" checked="checked" /> | |
| 3359 | + @ <input type="checkbox" name="c%d(tagid)" checked="checked"> | |
| 3361 | 3360 | }else{ |
| 3362 | - @ <input type="checkbox" name="c%d(tagid)" /> | |
| 3361 | + @ <input type="checkbox" name="c%d(tagid)"> | |
| 3363 | 3362 | } |
| 3364 | 3363 | if( isSpecialTag ){ |
| 3365 | 3364 | @ Cancel special tag <b>%h(zTagName)</b></label> |
| 3366 | 3365 | }else{ |
| 3367 | 3366 | @ Cancel tag <b>%h(&zTagName[4])</b></label> |
| @@ -3377,35 +3376,35 @@ | ||
| 3377 | 3376 | zNewBranch = zBranchName; |
| 3378 | 3377 | } |
| 3379 | 3378 | @ <tr><th align="right" valign="top">Branching:</th> |
| 3380 | 3379 | @ <td valign="top"> |
| 3381 | 3380 | @ <label><input id="newbr" type="checkbox" name="newbr" \ |
| 3382 | - @ data-branch='%h(zBranchName)'%s(zNewBrFlag) /> | |
| 3383 | - @ Make this check-in the start of a new branch named:</label> | |
| 3381 | + @ data-branch='%h(zBranchName)'%s(zNewBrFlag)> | |
| 3382 | + @ Starting from this check-in, rename the branch to:</label> | |
| 3384 | 3383 | @ <input id="brname" type="text" style="width:15;" name="brname" \ |
| 3385 | - @ value="%h(zNewBranch)" /></td></tr> | |
| 3384 | + @ value="%h(zNewBranch)"></td></tr> | |
| 3386 | 3385 | if( !fHasHidden ){ |
| 3387 | 3386 | @ <tr><th align="right" valign="top">Branch Hiding:</th> |
| 3388 | 3387 | @ <td valign="top"> |
| 3389 | - @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) /> | |
| 3388 | + @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag)> | |
| 3390 | 3389 | @ Hide branch |
| 3391 | 3390 | @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span> |
| 3392 | 3391 | @ from the timeline starting from this check-in</label> |
| 3393 | 3392 | @ </td></tr> |
| 3394 | 3393 | } |
| 3395 | 3394 | if( !fHasClosed ){ |
| 3396 | 3395 | if( is_a_leaf(rid) ){ |
| 3397 | 3396 | @ <tr><th align="right" valign="top">Leaf Closure:</th> |
| 3398 | 3397 | @ <td valign="top"> |
| 3399 | - @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> | |
| 3398 | + @ <label><input type="checkbox" name="close"%s(zCloseFlag)> | |
| 3400 | 3399 | @ Mark this leaf as "closed" so that it no longer appears on the |
| 3401 | 3400 | @ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label> |
| 3402 | 3401 | @ </td></tr> |
| 3403 | 3402 | }else if( zBranchName ){ |
| 3404 | 3403 | @ <tr><th align="right" valign="top">Branch Closure:</th> |
| 3405 | 3404 | @ <td valign="top"> |
| 3406 | - @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> | |
| 3405 | + @ <label><input type="checkbox" name="close"%s(zCloseFlag)> | |
| 3407 | 3406 | @ Mark branch |
| 3408 | 3407 | @ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span> |
| 3409 | 3408 | @ as "closed".</label> |
| 3410 | 3409 | @ </td></tr> |
| 3411 | 3410 | } |
| @@ -3412,14 +3411,14 @@ | ||
| 3412 | 3411 | } |
| 3413 | 3412 | if( zBranchName ) fossil_free(zBranchName); |
| 3414 | 3413 | |
| 3415 | 3414 | |
| 3416 | 3415 | @ <tr><td colspan="2"> |
| 3417 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 3418 | - @ <input type="submit" name="preview" value="Preview" /> | |
| 3416 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 3417 | + @ <input type="submit" name="preview" value="Preview"> | |
| 3419 | 3418 | if( P("preview") ){ |
| 3420 | - @ <input type="submit" name="apply" value="Apply Changes" /> | |
| 3419 | + @ <input type="submit" name="apply" value="Apply Changes"> | |
| 3421 | 3420 | } |
| 3422 | 3421 | @ </td></tr> |
| 3423 | 3422 | @ </table> |
| 3424 | 3423 | @ </div></form> |
| 3425 | 3424 | builtin_request_js("ci_edit.js"); |
| @@ -3478,11 +3477,11 @@ | ||
| 3478 | 3477 | ** --date DATETIME Make DATETIME the check-in time |
| 3479 | 3478 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3480 | 3479 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3481 | 3480 | ** --tag TAG Add new TAG to this check-in |
| 3482 | 3481 | ** --cancel TAG Cancel TAG from this check-in |
| 3483 | -** --branch NAME Make this check-in the start of branch NAME | |
| 3482 | +** --branch NAME Rename branch of check-in to NAME | |
| 3484 | 3483 | ** --hide Hide branch starting from this check-in |
| 3485 | 3484 | ** --close Mark this "leaf" as closed |
| 3486 | 3485 | ** -n|--dry-run Print control artifact, but make no changes |
| 3487 | 3486 | ** --date-override DATETIME Set the change time on the control artifact |
| 3488 | 3487 | ** --user-override USER Set the user name on the control artifact |
| 3489 | 3488 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1040,13 +1040,13 @@ | |
| 1040 | if( g.perm.ModWiki && modPending ){ |
| 1041 | @ <div class="section">Moderation</div> |
| 1042 | @ <blockquote> |
| 1043 | @ <form method="POST" action="%R/winfo/%s(zUuid)"> |
| 1044 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1045 | @ Delete this change</label><br /> |
| 1046 | @ <label><input type="radio" name="modaction" value="approve"> |
| 1047 | @ Approve this change</label><br /> |
| 1048 | @ <input type="submit" value="Submit"> |
| 1049 | @ </form> |
| 1050 | @ </blockquote> |
| 1051 | } |
| 1052 | |
| @@ -1296,11 +1296,11 @@ | |
| 1296 | @ are shown.</b></p> |
| 1297 | } |
| 1298 | if( zGlob ){ |
| 1299 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1300 | } |
| 1301 | @<hr /><p> |
| 1302 | } |
| 1303 | blob_reset(&qp); |
| 1304 | |
| 1305 | manifest_file_rewind(pFrom); |
| 1306 | pFileFrom = manifest_file_next(pFrom, 0); |
| @@ -1820,11 +1820,11 @@ | |
| 1820 | if( pRe ){ |
| 1821 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1822 | @ are shown.</b> |
| 1823 | DCfg.pRe = pRe; |
| 1824 | } |
| 1825 | @ <hr /> |
| 1826 | append_diff(zV1, zV2, &DCfg); |
| 1827 | append_diff_javascript(diffType); |
| 1828 | style_finish_page(); |
| 1829 | } |
| 1830 | |
| @@ -2128,11 +2128,11 @@ | |
| 2128 | blob_zero(&downloadName); |
| 2129 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 2130 | object_description(rid, objdescFlags, 0, &downloadName); |
| 2131 | style_submenu_element("Download", "%R/raw/%s?at=%T", |
| 2132 | zUuid, file_tail(blob_str(&downloadName))); |
| 2133 | @ <hr /> |
| 2134 | content_get(rid, &content); |
| 2135 | if( !g.isHuman ){ |
| 2136 | /* Prevent robots from running hexdump on megabyte-sized source files |
| 2137 | ** and there by eating up lots of CPU time and bandwidth. There is |
| 2138 | ** no good reason for a robot to need a hexdump. */ |
| @@ -2654,11 +2654,11 @@ | |
| 2654 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2655 | } |
| 2656 | if( descOnly ){ |
| 2657 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2658 | }else{ |
| 2659 | @ <hr /> |
| 2660 | content_get(rid, &content); |
| 2661 | if( renderAsWiki ){ |
| 2662 | safe_html_context(DOCSRC_FILE); |
| 2663 | wiki_render_by_mimetype(&content, zMime); |
| 2664 | document_emit_js(); |
| @@ -2801,11 +2801,11 @@ | |
| 2801 | } |
| 2802 | modPending = moderation_pending_www(rid); |
| 2803 | @ <tr><th>Ticket:</th> |
| 2804 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> |
| 2805 | if( zTktTitle ){ |
| 2806 | @<br />%h(zTktTitle) |
| 2807 | } |
| 2808 | @</td></tr> |
| 2809 | @ <tr><th>User & Date:</th><td> |
| 2810 | hyperlink_to_user(pTktChng->zUser, zDate, " on "); |
| 2811 | hyperlink_to_date(zDate, "</td></tr>"); |
| @@ -2816,13 +2816,13 @@ | |
| 2816 | if( g.perm.ModTkt && modPending ){ |
| 2817 | @ <div class="section">Moderation</div> |
| 2818 | @ <blockquote> |
| 2819 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 2820 | @ <label><input type="radio" name="modaction" value="delete"> |
| 2821 | @ Delete this change</label><br /> |
| 2822 | @ <label><input type="radio" name="modaction" value="approve"> |
| 2823 | @ Approve this change</label><br /> |
| 2824 | @ <input type="submit" value="Submit"> |
| 2825 | @ </form> |
| 2826 | @ </blockquote> |
| 2827 | } |
| 2828 | |
| @@ -3268,53 +3268,53 @@ | |
| 3268 | if( zChngTime ){ |
| 3269 | @ <p>The timestamp on the tag used to make the changes above |
| 3270 | @ will be overridden as: %s(date_in_standard_format(zChngTime))</p> |
| 3271 | } |
| 3272 | @ </blockquote> |
| 3273 | @ <hr /> |
| 3274 | blob_reset(&suffix); |
| 3275 | } |
| 3276 | @ <p>Make changes to attributes of check-in |
| 3277 | @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p> |
| 3278 | form_begin(0, "%R/ci_edit"); |
| 3279 | login_insert_csrf_secret(); |
| 3280 | @ <div><input type="hidden" name="r" value="%s(zUuid)" /> |
| 3281 | @ <table border="0" cellspacing="10"> |
| 3282 | |
| 3283 | @ <tr><th align="right" valign="top">User:</th> |
| 3284 | @ <td valign="top"> |
| 3285 | @ <input type="text" name="u" size="20" value="%h(zNewUser)" /> |
| 3286 | @ </td></tr> |
| 3287 | |
| 3288 | @ <tr><th align="right" valign="top">Comment:</th> |
| 3289 | @ <td valign="top"> |
| 3290 | @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea> |
| 3291 | @ </td></tr> |
| 3292 | |
| 3293 | @ <tr><th align="right" valign="top">Check-in Time:</th> |
| 3294 | @ <td valign="top"> |
| 3295 | @ <input type="text" name="dt" size="20" value="%h(zNewDate)" /> |
| 3296 | @ </td></tr> |
| 3297 | |
| 3298 | if( zChngTime ){ |
| 3299 | @ <tr><th align="right" valign="top">Timestamp of this change:</th> |
| 3300 | @ <td valign="top"> |
| 3301 | @ <input type="text" name="chngtime" size="20" value="%h(zChngTime)" /> |
| 3302 | @ </td></tr> |
| 3303 | } |
| 3304 | |
| 3305 | @ <tr><th align="right" valign="top">Background Color:</th> |
| 3306 | @ <td valign="top"> |
| 3307 | @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) /> |
| 3308 | @ Change background color: \ |
| 3309 | @ <input type='color' name='clr'\ |
| 3310 | @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div> |
| 3311 | @ <div><label> |
| 3312 | if( fNewPropagateColor ){ |
| 3313 | @ <input type="checkbox" name="pclr" checked="checked" /> |
| 3314 | }else{ |
| 3315 | @ <input type="checkbox" name="pclr" /> |
| 3316 | } |
| 3317 | @ Propagate color to descendants</label></div> |
| 3318 | @ <div class='font-size-80'>Be aware that fixed background |
| 3319 | @ colors will not interact well with all available skins. |
| 3320 | @ It is recommended that Fossil be allowed to select these |
| @@ -3322,14 +3322,13 @@ | |
| 3322 | @ preferences into account.</div> |
| 3323 | @ </td></tr> |
| 3324 | |
| 3325 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3326 | @ <td valign="top"> |
| 3327 | @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) /> |
| 3328 | @ Add the following new tag name to this check-in:</label> |
| 3329 | @ <input type="text" size='15' name="tagname" value="%h(zNewTag)" \ |
| 3330 | @ id='tagname' /> |
| 3331 | zBranchName = db_text(0, "SELECT value FROM tagxref, tag" |
| 3332 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3333 | " AND tagxref.tagid=%d", rid, TAG_BRANCH); |
| 3334 | db_prepare(&q, |
| 3335 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| @@ -3353,15 +3352,15 @@ | |
| 3353 | }else if( !isSpecialTag && zTagName && |
| 3354 | fossil_strcmp(&zTagName[4], zBranchName)==0){ |
| 3355 | continue; |
| 3356 | } |
| 3357 | sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); |
| 3358 | @ <br /><label> |
| 3359 | if( P(zLabel) ){ |
| 3360 | @ <input type="checkbox" name="c%d(tagid)" checked="checked" /> |
| 3361 | }else{ |
| 3362 | @ <input type="checkbox" name="c%d(tagid)" /> |
| 3363 | } |
| 3364 | if( isSpecialTag ){ |
| 3365 | @ Cancel special tag <b>%h(zTagName)</b></label> |
| 3366 | }else{ |
| 3367 | @ Cancel tag <b>%h(&zTagName[4])</b></label> |
| @@ -3377,35 +3376,35 @@ | |
| 3377 | zNewBranch = zBranchName; |
| 3378 | } |
| 3379 | @ <tr><th align="right" valign="top">Branching:</th> |
| 3380 | @ <td valign="top"> |
| 3381 | @ <label><input id="newbr" type="checkbox" name="newbr" \ |
| 3382 | @ data-branch='%h(zBranchName)'%s(zNewBrFlag) /> |
| 3383 | @ Make this check-in the start of a new branch named:</label> |
| 3384 | @ <input id="brname" type="text" style="width:15;" name="brname" \ |
| 3385 | @ value="%h(zNewBranch)" /></td></tr> |
| 3386 | if( !fHasHidden ){ |
| 3387 | @ <tr><th align="right" valign="top">Branch Hiding:</th> |
| 3388 | @ <td valign="top"> |
| 3389 | @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) /> |
| 3390 | @ Hide branch |
| 3391 | @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span> |
| 3392 | @ from the timeline starting from this check-in</label> |
| 3393 | @ </td></tr> |
| 3394 | } |
| 3395 | if( !fHasClosed ){ |
| 3396 | if( is_a_leaf(rid) ){ |
| 3397 | @ <tr><th align="right" valign="top">Leaf Closure:</th> |
| 3398 | @ <td valign="top"> |
| 3399 | @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> |
| 3400 | @ Mark this leaf as "closed" so that it no longer appears on the |
| 3401 | @ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label> |
| 3402 | @ </td></tr> |
| 3403 | }else if( zBranchName ){ |
| 3404 | @ <tr><th align="right" valign="top">Branch Closure:</th> |
| 3405 | @ <td valign="top"> |
| 3406 | @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> |
| 3407 | @ Mark branch |
| 3408 | @ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span> |
| 3409 | @ as "closed".</label> |
| 3410 | @ </td></tr> |
| 3411 | } |
| @@ -3412,14 +3411,14 @@ | |
| 3412 | } |
| 3413 | if( zBranchName ) fossil_free(zBranchName); |
| 3414 | |
| 3415 | |
| 3416 | @ <tr><td colspan="2"> |
| 3417 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 3418 | @ <input type="submit" name="preview" value="Preview" /> |
| 3419 | if( P("preview") ){ |
| 3420 | @ <input type="submit" name="apply" value="Apply Changes" /> |
| 3421 | } |
| 3422 | @ </td></tr> |
| 3423 | @ </table> |
| 3424 | @ </div></form> |
| 3425 | builtin_request_js("ci_edit.js"); |
| @@ -3478,11 +3477,11 @@ | |
| 3478 | ** --date DATETIME Make DATETIME the check-in time |
| 3479 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3480 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3481 | ** --tag TAG Add new TAG to this check-in |
| 3482 | ** --cancel TAG Cancel TAG from this check-in |
| 3483 | ** --branch NAME Make this check-in the start of branch NAME |
| 3484 | ** --hide Hide branch starting from this check-in |
| 3485 | ** --close Mark this "leaf" as closed |
| 3486 | ** -n|--dry-run Print control artifact, but make no changes |
| 3487 | ** --date-override DATETIME Set the change time on the control artifact |
| 3488 | ** --user-override USER Set the user name on the control artifact |
| 3489 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1040,13 +1040,13 @@ | |
| 1040 | if( g.perm.ModWiki && modPending ){ |
| 1041 | @ <div class="section">Moderation</div> |
| 1042 | @ <blockquote> |
| 1043 | @ <form method="POST" action="%R/winfo/%s(zUuid)"> |
| 1044 | @ <label><input type="radio" name="modaction" value="delete"> |
| 1045 | @ Delete this change</label><br> |
| 1046 | @ <label><input type="radio" name="modaction" value="approve"> |
| 1047 | @ Approve this change</label><br> |
| 1048 | @ <input type="submit" value="Submit"> |
| 1049 | @ </form> |
| 1050 | @ </blockquote> |
| 1051 | } |
| 1052 | |
| @@ -1296,11 +1296,11 @@ | |
| 1296 | @ are shown.</b></p> |
| 1297 | } |
| 1298 | if( zGlob ){ |
| 1299 | @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> |
| 1300 | } |
| 1301 | @<hr><p> |
| 1302 | } |
| 1303 | blob_reset(&qp); |
| 1304 | |
| 1305 | manifest_file_rewind(pFrom); |
| 1306 | pFileFrom = manifest_file_next(pFrom, 0); |
| @@ -1820,11 +1820,11 @@ | |
| 1820 | if( pRe ){ |
| 1821 | @ <b>Only differences that match regular expression "%h(zRe)" |
| 1822 | @ are shown.</b> |
| 1823 | DCfg.pRe = pRe; |
| 1824 | } |
| 1825 | @ <hr> |
| 1826 | append_diff(zV1, zV2, &DCfg); |
| 1827 | append_diff_javascript(diffType); |
| 1828 | style_finish_page(); |
| 1829 | } |
| 1830 | |
| @@ -2128,11 +2128,11 @@ | |
| 2128 | blob_zero(&downloadName); |
| 2129 | if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; |
| 2130 | object_description(rid, objdescFlags, 0, &downloadName); |
| 2131 | style_submenu_element("Download", "%R/raw/%s?at=%T", |
| 2132 | zUuid, file_tail(blob_str(&downloadName))); |
| 2133 | @ <hr> |
| 2134 | content_get(rid, &content); |
| 2135 | if( !g.isHuman ){ |
| 2136 | /* Prevent robots from running hexdump on megabyte-sized source files |
| 2137 | ** and there by eating up lots of CPU time and bandwidth. There is |
| 2138 | ** no good reason for a robot to need a hexdump. */ |
| @@ -2654,11 +2654,11 @@ | |
| 2654 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2655 | } |
| 2656 | if( descOnly ){ |
| 2657 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2658 | }else{ |
| 2659 | @ <hr> |
| 2660 | content_get(rid, &content); |
| 2661 | if( renderAsWiki ){ |
| 2662 | safe_html_context(DOCSRC_FILE); |
| 2663 | wiki_render_by_mimetype(&content, zMime); |
| 2664 | document_emit_js(); |
| @@ -2801,11 +2801,11 @@ | |
| 2801 | } |
| 2802 | modPending = moderation_pending_www(rid); |
| 2803 | @ <tr><th>Ticket:</th> |
| 2804 | @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> |
| 2805 | if( zTktTitle ){ |
| 2806 | @<br>%h(zTktTitle) |
| 2807 | } |
| 2808 | @</td></tr> |
| 2809 | @ <tr><th>User & Date:</th><td> |
| 2810 | hyperlink_to_user(pTktChng->zUser, zDate, " on "); |
| 2811 | hyperlink_to_date(zDate, "</td></tr>"); |
| @@ -2816,13 +2816,13 @@ | |
| 2816 | if( g.perm.ModTkt && modPending ){ |
| 2817 | @ <div class="section">Moderation</div> |
| 2818 | @ <blockquote> |
| 2819 | @ <form method="POST" action="%R/tinfo/%s(zUuid)"> |
| 2820 | @ <label><input type="radio" name="modaction" value="delete"> |
| 2821 | @ Delete this change</label><br> |
| 2822 | @ <label><input type="radio" name="modaction" value="approve"> |
| 2823 | @ Approve this change</label><br> |
| 2824 | @ <input type="submit" value="Submit"> |
| 2825 | @ </form> |
| 2826 | @ </blockquote> |
| 2827 | } |
| 2828 | |
| @@ -3268,53 +3268,53 @@ | |
| 3268 | if( zChngTime ){ |
| 3269 | @ <p>The timestamp on the tag used to make the changes above |
| 3270 | @ will be overridden as: %s(date_in_standard_format(zChngTime))</p> |
| 3271 | } |
| 3272 | @ </blockquote> |
| 3273 | @ <hr> |
| 3274 | blob_reset(&suffix); |
| 3275 | } |
| 3276 | @ <p>Make changes to attributes of check-in |
| 3277 | @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p> |
| 3278 | form_begin(0, "%R/ci_edit"); |
| 3279 | login_insert_csrf_secret(); |
| 3280 | @ <div><input type="hidden" name="r" value="%s(zUuid)"> |
| 3281 | @ <table border="0" cellspacing="10"> |
| 3282 | |
| 3283 | @ <tr><th align="right" valign="top">User:</th> |
| 3284 | @ <td valign="top"> |
| 3285 | @ <input type="text" name="u" size="20" value="%h(zNewUser)"> |
| 3286 | @ </td></tr> |
| 3287 | |
| 3288 | @ <tr><th align="right" valign="top">Comment:</th> |
| 3289 | @ <td valign="top"> |
| 3290 | @ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea> |
| 3291 | @ </td></tr> |
| 3292 | |
| 3293 | @ <tr><th align="right" valign="top">Check-in Time:</th> |
| 3294 | @ <td valign="top"> |
| 3295 | @ <input type="text" name="dt" size="20" value="%h(zNewDate)"> |
| 3296 | @ </td></tr> |
| 3297 | |
| 3298 | if( zChngTime ){ |
| 3299 | @ <tr><th align="right" valign="top">Timestamp of this change:</th> |
| 3300 | @ <td valign="top"> |
| 3301 | @ <input type="text" name="chngtime" size="20" value="%h(zChngTime)"> |
| 3302 | @ </td></tr> |
| 3303 | } |
| 3304 | |
| 3305 | @ <tr><th align="right" valign="top">Background Color:</th> |
| 3306 | @ <td valign="top"> |
| 3307 | @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag)> |
| 3308 | @ Change background color: \ |
| 3309 | @ <input type='color' name='clr'\ |
| 3310 | @ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div> |
| 3311 | @ <div><label> |
| 3312 | if( fNewPropagateColor ){ |
| 3313 | @ <input type="checkbox" name="pclr" checked="checked"> |
| 3314 | }else{ |
| 3315 | @ <input type="checkbox" name="pclr"> |
| 3316 | } |
| 3317 | @ Propagate color to descendants</label></div> |
| 3318 | @ <div class='font-size-80'>Be aware that fixed background |
| 3319 | @ colors will not interact well with all available skins. |
| 3320 | @ It is recommended that Fossil be allowed to select these |
| @@ -3322,14 +3322,13 @@ | |
| 3322 | @ preferences into account.</div> |
| 3323 | @ </td></tr> |
| 3324 | |
| 3325 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3326 | @ <td valign="top"> |
| 3327 | @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> |
| 3328 | @ Add the following new tag name to this check-in:</label> |
| 3329 | @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> |
| 3330 | zBranchName = db_text(0, "SELECT value FROM tagxref, tag" |
| 3331 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3332 | " AND tagxref.tagid=%d", rid, TAG_BRANCH); |
| 3333 | db_prepare(&q, |
| 3334 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| @@ -3353,15 +3352,15 @@ | |
| 3352 | }else if( !isSpecialTag && zTagName && |
| 3353 | fossil_strcmp(&zTagName[4], zBranchName)==0){ |
| 3354 | continue; |
| 3355 | } |
| 3356 | sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); |
| 3357 | @ <br><label> |
| 3358 | if( P(zLabel) ){ |
| 3359 | @ <input type="checkbox" name="c%d(tagid)" checked="checked"> |
| 3360 | }else{ |
| 3361 | @ <input type="checkbox" name="c%d(tagid)"> |
| 3362 | } |
| 3363 | if( isSpecialTag ){ |
| 3364 | @ Cancel special tag <b>%h(zTagName)</b></label> |
| 3365 | }else{ |
| 3366 | @ Cancel tag <b>%h(&zTagName[4])</b></label> |
| @@ -3377,35 +3376,35 @@ | |
| 3376 | zNewBranch = zBranchName; |
| 3377 | } |
| 3378 | @ <tr><th align="right" valign="top">Branching:</th> |
| 3379 | @ <td valign="top"> |
| 3380 | @ <label><input id="newbr" type="checkbox" name="newbr" \ |
| 3381 | @ data-branch='%h(zBranchName)'%s(zNewBrFlag)> |
| 3382 | @ Starting from this check-in, rename the branch to:</label> |
| 3383 | @ <input id="brname" type="text" style="width:15;" name="brname" \ |
| 3384 | @ value="%h(zNewBranch)"></td></tr> |
| 3385 | if( !fHasHidden ){ |
| 3386 | @ <tr><th align="right" valign="top">Branch Hiding:</th> |
| 3387 | @ <td valign="top"> |
| 3388 | @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag)> |
| 3389 | @ Hide branch |
| 3390 | @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span> |
| 3391 | @ from the timeline starting from this check-in</label> |
| 3392 | @ </td></tr> |
| 3393 | } |
| 3394 | if( !fHasClosed ){ |
| 3395 | if( is_a_leaf(rid) ){ |
| 3396 | @ <tr><th align="right" valign="top">Leaf Closure:</th> |
| 3397 | @ <td valign="top"> |
| 3398 | @ <label><input type="checkbox" name="close"%s(zCloseFlag)> |
| 3399 | @ Mark this leaf as "closed" so that it no longer appears on the |
| 3400 | @ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label> |
| 3401 | @ </td></tr> |
| 3402 | }else if( zBranchName ){ |
| 3403 | @ <tr><th align="right" valign="top">Branch Closure:</th> |
| 3404 | @ <td valign="top"> |
| 3405 | @ <label><input type="checkbox" name="close"%s(zCloseFlag)> |
| 3406 | @ Mark branch |
| 3407 | @ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span> |
| 3408 | @ as "closed".</label> |
| 3409 | @ </td></tr> |
| 3410 | } |
| @@ -3412,14 +3411,14 @@ | |
| 3411 | } |
| 3412 | if( zBranchName ) fossil_free(zBranchName); |
| 3413 | |
| 3414 | |
| 3415 | @ <tr><td colspan="2"> |
| 3416 | @ <input type="submit" name="cancel" value="Cancel"> |
| 3417 | @ <input type="submit" name="preview" value="Preview"> |
| 3418 | if( P("preview") ){ |
| 3419 | @ <input type="submit" name="apply" value="Apply Changes"> |
| 3420 | } |
| 3421 | @ </td></tr> |
| 3422 | @ </table> |
| 3423 | @ </div></form> |
| 3424 | builtin_request_js("ci_edit.js"); |
| @@ -3478,11 +3477,11 @@ | |
| 3477 | ** --date DATETIME Make DATETIME the check-in time |
| 3478 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3479 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3480 | ** --tag TAG Add new TAG to this check-in |
| 3481 | ** --cancel TAG Cancel TAG from this check-in |
| 3482 | ** --branch NAME Rename branch of check-in to NAME |
| 3483 | ** --hide Hide branch starting from this check-in |
| 3484 | ** --close Mark this "leaf" as closed |
| 3485 | ** -n|--dry-run Print control artifact, but make no changes |
| 3486 | ** --date-override DATETIME Set the change time on the control artifact |
| 3487 | ** --user-override USER Set the user name on the control artifact |
| 3488 |
+1
-1
| --- src/loadctrl.c | ||
| +++ src/loadctrl.c | ||
| @@ -65,11 +65,11 @@ | ||
| 65 | 65 | |
| 66 | 66 | style_set_current_feature("test"); |
| 67 | 67 | style_header("Server Overload"); |
| 68 | 68 | @ <h2>The server load is currently too high. |
| 69 | 69 | @ Please try again later.</h2> |
| 70 | - @ <p>Current load average: %f(load_average()).<br /> | |
| 70 | + @ <p>Current load average: %f(load_average()).<br> | |
| 71 | 71 | @ Load average limit: %f(mxLoad)</p> |
| 72 | 72 | style_finish_page(); |
| 73 | 73 | cgi_set_status(503,"Server Overload"); |
| 74 | 74 | cgi_reply(); |
| 75 | 75 | exit(0); |
| 76 | 76 |
| --- src/loadctrl.c | |
| +++ src/loadctrl.c | |
| @@ -65,11 +65,11 @@ | |
| 65 | |
| 66 | style_set_current_feature("test"); |
| 67 | style_header("Server Overload"); |
| 68 | @ <h2>The server load is currently too high. |
| 69 | @ Please try again later.</h2> |
| 70 | @ <p>Current load average: %f(load_average()).<br /> |
| 71 | @ Load average limit: %f(mxLoad)</p> |
| 72 | style_finish_page(); |
| 73 | cgi_set_status(503,"Server Overload"); |
| 74 | cgi_reply(); |
| 75 | exit(0); |
| 76 |
| --- src/loadctrl.c | |
| +++ src/loadctrl.c | |
| @@ -65,11 +65,11 @@ | |
| 65 | |
| 66 | style_set_current_feature("test"); |
| 67 | style_header("Server Overload"); |
| 68 | @ <h2>The server load is currently too high. |
| 69 | @ Please try again later.</h2> |
| 70 | @ <p>Current load average: %f(load_average()).<br> |
| 71 | @ Load average limit: %f(mxLoad)</p> |
| 72 | style_finish_page(); |
| 73 | cgi_set_status(503,"Server Overload"); |
| 74 | cgi_reply(); |
| 75 | exit(0); |
| 76 |
+19
-19
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -737,14 +737,14 @@ | ||
| 737 | 737 | form_begin(0, "https:%s/login", g.zBaseURL+5); |
| 738 | 738 | }else{ |
| 739 | 739 | form_begin(0, "%R/login"); |
| 740 | 740 | } |
| 741 | 741 | if( zGoto ){ |
| 742 | - @ <input type="hidden" name="g" value="%h(zGoto)" /> | |
| 742 | + @ <input type="hidden" name="g" value="%h(zGoto)"> | |
| 743 | 743 | } |
| 744 | 744 | if( anonFlag ){ |
| 745 | - @ <input type="hidden" name="anon" value="1" /> | |
| 745 | + @ <input type="hidden" name="anon" value="1"> | |
| 746 | 746 | } |
| 747 | 747 | if( g.zLogin ){ |
| 748 | 748 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 749 | 749 | @ <input type="submit" name="out" value="Logout"></p> |
| 750 | 750 | @ </form> |
| @@ -775,11 +775,11 @@ | ||
| 775 | 775 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 776 | 776 | @ </tr> |
| 777 | 777 | @ <tr> |
| 778 | 778 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 779 | 779 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 780 | - @ name="p" value="" size="30" />\ | |
| 780 | + @ name="p" value="" size="30">\ | |
| 781 | 781 | if( zAnonPw && !noAnon ){ |
| 782 | 782 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 783 | 783 | } |
| 784 | 784 | @ </td> |
| 785 | 785 | @ </tr> |
| @@ -809,20 +809,20 @@ | ||
| 809 | 809 | if( zAnonPw && !noAnon ){ |
| 810 | 810 | const char *zDecoded = captcha_decode(uSeed); |
| 811 | 811 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 812 | 812 | char *zCaptcha = captcha_render(zDecoded); |
| 813 | 813 | |
| 814 | - @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> | |
| 814 | + @ <p><input type="hidden" name="cs" value="%u(uSeed)"> | |
| 815 | 815 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 816 | 816 | @ the 8-character hexadecimal password shown below:</p> |
| 817 | 817 | @ <div class="captcha"><table class="captcha"><tr><td>\ |
| 818 | 818 | @ <pre class="captcha"> |
| 819 | 819 | @ %h(zCaptcha) |
| 820 | 820 | @ </pre></td></tr></table> |
| 821 | 821 | if( bAutoCaptcha ) { |
| 822 | 822 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 823 | - @ data-af='%s(zDecoded)' /> | |
| 823 | + @ data-af='%s(zDecoded)'> | |
| 824 | 824 | builtin_request_js("login.js"); |
| 825 | 825 | } |
| 826 | 826 | @ </div> |
| 827 | 827 | free(zCaptcha); |
| 828 | 828 | } |
| @@ -848,16 +848,16 @@ | ||
| 848 | 848 | @ <tr><td class="form_label" id="oldpw">Old Password:</td> |
| 849 | 849 | @ <td><input aria-labelledby="oldpw" type="password" name="p" \ |
| 850 | 850 | @ size="30"/></td></tr> |
| 851 | 851 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 852 | 852 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 853 | - @ size="30" /> Suggestion: %z(zRPW)</td></tr> | |
| 853 | + @ size="30"> Suggestion: %z(zRPW)</td></tr> | |
| 854 | 854 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 855 | 855 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 856 | - @ size="30" /></td></tr> | |
| 856 | + @ size="30"></td></tr> | |
| 857 | 857 | @ <tr><td></td> |
| 858 | - @ <td><input type="submit" value="Change Password" /></td></tr> | |
| 858 | + @ <td><input type="submit" value="Change Password"></td></tr> | |
| 859 | 859 | @ </table> |
| 860 | 860 | @ </form> |
| 861 | 861 | } |
| 862 | 862 | } |
| 863 | 863 | style_finish_page(); |
| @@ -1085,16 +1085,16 @@ | ||
| 1085 | 1085 | form_begin(0, "%R/resetpw"); |
| 1086 | 1086 | @ <input type='hidden' name='name' value='%h(zName)'> |
| 1087 | 1087 | @ <table> |
| 1088 | 1088 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 1089 | 1089 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 1090 | - @ size="30" /> Suggestion: %z(zRPW)</td></tr> | |
| 1090 | + @ size="30"> Suggestion: %z(zRPW)</td></tr> | |
| 1091 | 1091 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 1092 | 1092 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 1093 | - @ size="30" /></td></tr> | |
| 1093 | + @ size="30"></td></tr> | |
| 1094 | 1094 | @ <tr><td></td> |
| 1095 | - @ <td><input type="submit" value="Change Password" /></td></tr> | |
| 1095 | + @ <td><input type="submit" value="Change Password"></td></tr> | |
| 1096 | 1096 | @ </table> |
| 1097 | 1097 | @ </form> |
| 1098 | 1098 | style_finish_page(); |
| 1099 | 1099 | } |
| 1100 | 1100 | |
| @@ -1790,11 +1790,11 @@ | ||
| 1790 | 1790 | ** logging in as anonymous. |
| 1791 | 1791 | */ |
| 1792 | 1792 | void login_anonymous_available(void){ |
| 1793 | 1793 | if( !g.perm.Hyperlink && g.anon.Hyperlink ){ |
| 1794 | 1794 | const char *zUrl = PD("PATH_INFO", ""); |
| 1795 | - @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br /> | |
| 1795 | + @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br> | |
| 1796 | 1796 | @ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a> |
| 1797 | 1797 | @ to enable hyperlinks.</p> |
| 1798 | 1798 | } |
| 1799 | 1799 | } |
| 1800 | 1800 | |
| @@ -1801,11 +1801,11 @@ | ||
| 1801 | 1801 | /* |
| 1802 | 1802 | ** While rendering a form, call this routine to add the Anti-CSRF token |
| 1803 | 1803 | ** as a hidden element of the form. |
| 1804 | 1804 | */ |
| 1805 | 1805 | void login_insert_csrf_secret(void){ |
| 1806 | - @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" /> | |
| 1806 | + @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> | |
| 1807 | 1807 | } |
| 1808 | 1808 | |
| 1809 | 1809 | /* |
| 1810 | 1810 | ** Before using the results of a form, first call this routine to verify |
| 1811 | 1811 | ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token |
| @@ -2123,13 +2123,13 @@ | ||
| 2123 | 2123 | style_header("Register"); |
| 2124 | 2124 | /* Print out the registration form. */ |
| 2125 | 2125 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2126 | 2126 | form_begin(0, "%R/register"); |
| 2127 | 2127 | if( P("g") ){ |
| 2128 | - @ <input type="hidden" name="g" value="%h(P("g"))" /> | |
| 2128 | + @ <input type="hidden" name="g" value="%h(P("g"))"> | |
| 2129 | 2129 | } |
| 2130 | - @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> | |
| 2130 | + @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> | |
| 2131 | 2131 | @ <table class="login_out"> |
| 2132 | 2132 | @ <tr> |
| 2133 | 2133 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2134 | 2134 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2135 | 2135 | @ value="%h(zUserID)" size="30"></td> |
| @@ -2152,11 +2152,11 @@ | ||
| 2152 | 2152 | @ value="%h(zEAddr)" size="30"></td> |
| 2153 | 2153 | @ </tr> |
| 2154 | 2154 | if( iErrLine==3 ){ |
| 2155 | 2155 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span> |
| 2156 | 2156 | if( uid>0 && login_self_password_reset_available() ){ |
| 2157 | - @ <br /> | |
| 2157 | + @ <br> | |
| 2158 | 2158 | @ <input type="submit" name="pwreset" \ |
| 2159 | 2159 | @ value="Request Password Reset For %h(zEAddr)"> |
| 2160 | 2160 | } |
| 2161 | 2161 | @ </td></tr> |
| 2162 | 2162 | } |
| @@ -2200,11 +2200,11 @@ | ||
| 2200 | 2200 | @ </tr> |
| 2201 | 2201 | if( iErrLine==6 ){ |
| 2202 | 2202 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2203 | 2203 | } |
| 2204 | 2204 | @ <tr><td></td> |
| 2205 | - @ <td><input type="submit" name="new" value="Register" /></td></tr> | |
| 2205 | + @ <td><input type="submit" name="new" value="Register"></td></tr> | |
| 2206 | 2206 | @ </table> |
| 2207 | 2207 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 2208 | 2208 | @ %h(zCaptcha) |
| 2209 | 2209 | @ </pre> |
| 2210 | 2210 | @ Enter this 8-letter code in the "Captcha" box above. |
| @@ -2328,12 +2328,12 @@ | ||
| 2328 | 2328 | |
| 2329 | 2329 | style_header("Request Password Reset"); |
| 2330 | 2330 | /* Print out the registration form. */ |
| 2331 | 2331 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2332 | 2332 | form_begin(0, "%R/reqpwreset"); |
| 2333 | - @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> | |
| 2334 | - @ <p><input type="hidden" name="reqpwreset" value="1" /> | |
| 2333 | + @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> | |
| 2334 | + @ <p><input type="hidden" name="reqpwreset" value="1"> | |
| 2335 | 2335 | @ <table class="login_out"> |
| 2336 | 2336 | @ <tr> |
| 2337 | 2337 | @ <td class="form_label" align="right" id="emaddr">Email Address:</td> |
| 2338 | 2338 | @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ |
| 2339 | 2339 | @ value="%h(zEAddr)" size="30"></td> |
| 2340 | 2340 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -737,14 +737,14 @@ | |
| 737 | form_begin(0, "https:%s/login", g.zBaseURL+5); |
| 738 | }else{ |
| 739 | form_begin(0, "%R/login"); |
| 740 | } |
| 741 | if( zGoto ){ |
| 742 | @ <input type="hidden" name="g" value="%h(zGoto)" /> |
| 743 | } |
| 744 | if( anonFlag ){ |
| 745 | @ <input type="hidden" name="anon" value="1" /> |
| 746 | } |
| 747 | if( g.zLogin ){ |
| 748 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 749 | @ <input type="submit" name="out" value="Logout"></p> |
| 750 | @ </form> |
| @@ -775,11 +775,11 @@ | |
| 775 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 776 | @ </tr> |
| 777 | @ <tr> |
| 778 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 779 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 780 | @ name="p" value="" size="30" />\ |
| 781 | if( zAnonPw && !noAnon ){ |
| 782 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 783 | } |
| 784 | @ </td> |
| 785 | @ </tr> |
| @@ -809,20 +809,20 @@ | |
| 809 | if( zAnonPw && !noAnon ){ |
| 810 | const char *zDecoded = captcha_decode(uSeed); |
| 811 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 812 | char *zCaptcha = captcha_render(zDecoded); |
| 813 | |
| 814 | @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> |
| 815 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 816 | @ the 8-character hexadecimal password shown below:</p> |
| 817 | @ <div class="captcha"><table class="captcha"><tr><td>\ |
| 818 | @ <pre class="captcha"> |
| 819 | @ %h(zCaptcha) |
| 820 | @ </pre></td></tr></table> |
| 821 | if( bAutoCaptcha ) { |
| 822 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 823 | @ data-af='%s(zDecoded)' /> |
| 824 | builtin_request_js("login.js"); |
| 825 | } |
| 826 | @ </div> |
| 827 | free(zCaptcha); |
| 828 | } |
| @@ -848,16 +848,16 @@ | |
| 848 | @ <tr><td class="form_label" id="oldpw">Old Password:</td> |
| 849 | @ <td><input aria-labelledby="oldpw" type="password" name="p" \ |
| 850 | @ size="30"/></td></tr> |
| 851 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 852 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 853 | @ size="30" /> Suggestion: %z(zRPW)</td></tr> |
| 854 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 855 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 856 | @ size="30" /></td></tr> |
| 857 | @ <tr><td></td> |
| 858 | @ <td><input type="submit" value="Change Password" /></td></tr> |
| 859 | @ </table> |
| 860 | @ </form> |
| 861 | } |
| 862 | } |
| 863 | style_finish_page(); |
| @@ -1085,16 +1085,16 @@ | |
| 1085 | form_begin(0, "%R/resetpw"); |
| 1086 | @ <input type='hidden' name='name' value='%h(zName)'> |
| 1087 | @ <table> |
| 1088 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 1089 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 1090 | @ size="30" /> Suggestion: %z(zRPW)</td></tr> |
| 1091 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 1092 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 1093 | @ size="30" /></td></tr> |
| 1094 | @ <tr><td></td> |
| 1095 | @ <td><input type="submit" value="Change Password" /></td></tr> |
| 1096 | @ </table> |
| 1097 | @ </form> |
| 1098 | style_finish_page(); |
| 1099 | } |
| 1100 | |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | ** logging in as anonymous. |
| 1791 | */ |
| 1792 | void login_anonymous_available(void){ |
| 1793 | if( !g.perm.Hyperlink && g.anon.Hyperlink ){ |
| 1794 | const char *zUrl = PD("PATH_INFO", ""); |
| 1795 | @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br /> |
| 1796 | @ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a> |
| 1797 | @ to enable hyperlinks.</p> |
| 1798 | } |
| 1799 | } |
| 1800 | |
| @@ -1801,11 +1801,11 @@ | |
| 1801 | /* |
| 1802 | ** While rendering a form, call this routine to add the Anti-CSRF token |
| 1803 | ** as a hidden element of the form. |
| 1804 | */ |
| 1805 | void login_insert_csrf_secret(void){ |
| 1806 | @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" /> |
| 1807 | } |
| 1808 | |
| 1809 | /* |
| 1810 | ** Before using the results of a form, first call this routine to verify |
| 1811 | ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token |
| @@ -2123,13 +2123,13 @@ | |
| 2123 | style_header("Register"); |
| 2124 | /* Print out the registration form. */ |
| 2125 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2126 | form_begin(0, "%R/register"); |
| 2127 | if( P("g") ){ |
| 2128 | @ <input type="hidden" name="g" value="%h(P("g"))" /> |
| 2129 | } |
| 2130 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 2131 | @ <table class="login_out"> |
| 2132 | @ <tr> |
| 2133 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2134 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2135 | @ value="%h(zUserID)" size="30"></td> |
| @@ -2152,11 +2152,11 @@ | |
| 2152 | @ value="%h(zEAddr)" size="30"></td> |
| 2153 | @ </tr> |
| 2154 | if( iErrLine==3 ){ |
| 2155 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span> |
| 2156 | if( uid>0 && login_self_password_reset_available() ){ |
| 2157 | @ <br /> |
| 2158 | @ <input type="submit" name="pwreset" \ |
| 2159 | @ value="Request Password Reset For %h(zEAddr)"> |
| 2160 | } |
| 2161 | @ </td></tr> |
| 2162 | } |
| @@ -2200,11 +2200,11 @@ | |
| 2200 | @ </tr> |
| 2201 | if( iErrLine==6 ){ |
| 2202 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2203 | } |
| 2204 | @ <tr><td></td> |
| 2205 | @ <td><input type="submit" name="new" value="Register" /></td></tr> |
| 2206 | @ </table> |
| 2207 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 2208 | @ %h(zCaptcha) |
| 2209 | @ </pre> |
| 2210 | @ Enter this 8-letter code in the "Captcha" box above. |
| @@ -2328,12 +2328,12 @@ | |
| 2328 | |
| 2329 | style_header("Request Password Reset"); |
| 2330 | /* Print out the registration form. */ |
| 2331 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2332 | form_begin(0, "%R/reqpwreset"); |
| 2333 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" /> |
| 2334 | @ <p><input type="hidden" name="reqpwreset" value="1" /> |
| 2335 | @ <table class="login_out"> |
| 2336 | @ <tr> |
| 2337 | @ <td class="form_label" align="right" id="emaddr">Email Address:</td> |
| 2338 | @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ |
| 2339 | @ value="%h(zEAddr)" size="30"></td> |
| 2340 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -737,14 +737,14 @@ | |
| 737 | form_begin(0, "https:%s/login", g.zBaseURL+5); |
| 738 | }else{ |
| 739 | form_begin(0, "%R/login"); |
| 740 | } |
| 741 | if( zGoto ){ |
| 742 | @ <input type="hidden" name="g" value="%h(zGoto)"> |
| 743 | } |
| 744 | if( anonFlag ){ |
| 745 | @ <input type="hidden" name="anon" value="1"> |
| 746 | } |
| 747 | if( g.zLogin ){ |
| 748 | @ <p>Currently logged in as <b>%h(g.zLogin)</b>. |
| 749 | @ <input type="submit" name="out" value="Logout"></p> |
| 750 | @ </form> |
| @@ -775,11 +775,11 @@ | |
| 775 | @ size="30" value="%s(anonFlag?"anonymous":"")"></td> |
| 776 | @ </tr> |
| 777 | @ <tr> |
| 778 | @ <td class="form_label" id="pswdlabel">Password:</td> |
| 779 | @ <td><input aria-labelledby="pswdlabel" type="password" id="p" \ |
| 780 | @ name="p" value="" size="30">\ |
| 781 | if( zAnonPw && !noAnon ){ |
| 782 | captcha_speakit_button(uSeed, "Speak password for \"anonymous\""); |
| 783 | } |
| 784 | @ </td> |
| 785 | @ </tr> |
| @@ -809,20 +809,20 @@ | |
| 809 | if( zAnonPw && !noAnon ){ |
| 810 | const char *zDecoded = captcha_decode(uSeed); |
| 811 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 812 | char *zCaptcha = captcha_render(zDecoded); |
| 813 | |
| 814 | @ <p><input type="hidden" name="cs" value="%u(uSeed)"> |
| 815 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| 816 | @ the 8-character hexadecimal password shown below:</p> |
| 817 | @ <div class="captcha"><table class="captcha"><tr><td>\ |
| 818 | @ <pre class="captcha"> |
| 819 | @ %h(zCaptcha) |
| 820 | @ </pre></td></tr></table> |
| 821 | if( bAutoCaptcha ) { |
| 822 | @ <input type="button" value="Fill out captcha" id='autofillButton' \ |
| 823 | @ data-af='%s(zDecoded)'> |
| 824 | builtin_request_js("login.js"); |
| 825 | } |
| 826 | @ </div> |
| 827 | free(zCaptcha); |
| 828 | } |
| @@ -848,16 +848,16 @@ | |
| 848 | @ <tr><td class="form_label" id="oldpw">Old Password:</td> |
| 849 | @ <td><input aria-labelledby="oldpw" type="password" name="p" \ |
| 850 | @ size="30"/></td></tr> |
| 851 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 852 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 853 | @ size="30"> Suggestion: %z(zRPW)</td></tr> |
| 854 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 855 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 856 | @ size="30"></td></tr> |
| 857 | @ <tr><td></td> |
| 858 | @ <td><input type="submit" value="Change Password"></td></tr> |
| 859 | @ </table> |
| 860 | @ </form> |
| 861 | } |
| 862 | } |
| 863 | style_finish_page(); |
| @@ -1085,16 +1085,16 @@ | |
| 1085 | form_begin(0, "%R/resetpw"); |
| 1086 | @ <input type='hidden' name='name' value='%h(zName)'> |
| 1087 | @ <table> |
| 1088 | @ <tr><td class="form_label" id="newpw">New Password:</td> |
| 1089 | @ <td><input aria-labelledby="newpw" type="password" name="n1" \ |
| 1090 | @ size="30"> Suggestion: %z(zRPW)</td></tr> |
| 1091 | @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> |
| 1092 | @ <td><input aria-labledby="reppw" type="password" name="n2" \ |
| 1093 | @ size="30"></td></tr> |
| 1094 | @ <tr><td></td> |
| 1095 | @ <td><input type="submit" value="Change Password"></td></tr> |
| 1096 | @ </table> |
| 1097 | @ </form> |
| 1098 | style_finish_page(); |
| 1099 | } |
| 1100 | |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | ** logging in as anonymous. |
| 1791 | */ |
| 1792 | void login_anonymous_available(void){ |
| 1793 | if( !g.perm.Hyperlink && g.anon.Hyperlink ){ |
| 1794 | const char *zUrl = PD("PATH_INFO", ""); |
| 1795 | @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br> |
| 1796 | @ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a> |
| 1797 | @ to enable hyperlinks.</p> |
| 1798 | } |
| 1799 | } |
| 1800 | |
| @@ -1801,11 +1801,11 @@ | |
| 1801 | /* |
| 1802 | ** While rendering a form, call this routine to add the Anti-CSRF token |
| 1803 | ** as a hidden element of the form. |
| 1804 | */ |
| 1805 | void login_insert_csrf_secret(void){ |
| 1806 | @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> |
| 1807 | } |
| 1808 | |
| 1809 | /* |
| 1810 | ** Before using the results of a form, first call this routine to verify |
| 1811 | ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token |
| @@ -2123,13 +2123,13 @@ | |
| 2123 | style_header("Register"); |
| 2124 | /* Print out the registration form. */ |
| 2125 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2126 | form_begin(0, "%R/register"); |
| 2127 | if( P("g") ){ |
| 2128 | @ <input type="hidden" name="g" value="%h(P("g"))"> |
| 2129 | } |
| 2130 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 2131 | @ <table class="login_out"> |
| 2132 | @ <tr> |
| 2133 | @ <td class="form_label" align="right" id="uid">User ID:</td> |
| 2134 | @ <td><input aria-labelledby="uid" type="text" name="u" \ |
| 2135 | @ value="%h(zUserID)" size="30"></td> |
| @@ -2152,11 +2152,11 @@ | |
| 2152 | @ value="%h(zEAddr)" size="30"></td> |
| 2153 | @ </tr> |
| 2154 | if( iErrLine==3 ){ |
| 2155 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span> |
| 2156 | if( uid>0 && login_self_password_reset_available() ){ |
| 2157 | @ <br> |
| 2158 | @ <input type="submit" name="pwreset" \ |
| 2159 | @ value="Request Password Reset For %h(zEAddr)"> |
| 2160 | } |
| 2161 | @ </td></tr> |
| 2162 | } |
| @@ -2200,11 +2200,11 @@ | |
| 2200 | @ </tr> |
| 2201 | if( iErrLine==6 ){ |
| 2202 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 2203 | } |
| 2204 | @ <tr><td></td> |
| 2205 | @ <td><input type="submit" name="new" value="Register"></td></tr> |
| 2206 | @ </table> |
| 2207 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 2208 | @ %h(zCaptcha) |
| 2209 | @ </pre> |
| 2210 | @ Enter this 8-letter code in the "Captcha" box above. |
| @@ -2328,12 +2328,12 @@ | |
| 2328 | |
| 2329 | style_header("Request Password Reset"); |
| 2330 | /* Print out the registration form. */ |
| 2331 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2332 | form_begin(0, "%R/reqpwreset"); |
| 2333 | @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)"> |
| 2334 | @ <p><input type="hidden" name="reqpwreset" value="1"> |
| 2335 | @ <table class="login_out"> |
| 2336 | @ <tr> |
| 2337 | @ <td class="form_label" align="right" id="emaddr">Email Address:</td> |
| 2338 | @ <td><input aria-labelledby="emaddr" type="text" name="ea" \ |
| 2339 | @ value="%h(zEAddr)" size="30"></td> |
| 2340 |
+63
-37
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -24,10 +24,20 @@ | ||
| 24 | 24 | # include <windows.h> |
| 25 | 25 | # include <io.h> |
| 26 | 26 | # define isatty(h) _isatty(h) |
| 27 | 27 | # define GETPID (int)GetCurrentProcessId |
| 28 | 28 | #endif |
| 29 | + | |
| 30 | +/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ | |
| 31 | +#if USE_SEE | |
| 32 | +#if defined(_WIN32) | |
| 33 | +typedef DWORD PID_T; | |
| 34 | +#else | |
| 35 | +typedef pid_t PID_T; | |
| 36 | +#endif | |
| 37 | +#endif | |
| 38 | + | |
| 29 | 39 | #include "main.h" |
| 30 | 40 | #include <string.h> |
| 31 | 41 | #include <time.h> |
| 32 | 42 | #include <fcntl.h> |
| 33 | 43 | #include <sys/types.h> |
| @@ -216,13 +226,13 @@ | ||
| 216 | 226 | const char *zLogin; /* Login name. NULL or "" if not logged in. */ |
| 217 | 227 | const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ |
| 218 | 228 | const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ |
| 219 | 229 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 220 | 230 | ** SSL client identity */ |
| 221 | -#if defined(_WIN32) && USE_SEE | |
| 231 | +#if USE_SEE | |
| 222 | 232 | const char *zPidKey; /* Saved value of the --usepidkey option. Only |
| 223 | - * applicable when using SEE on Windows. */ | |
| 233 | + * applicable when using SEE on Windows or Linux. */ | |
| 224 | 234 | #endif |
| 225 | 235 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 226 | 236 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 227 | 237 | int userUid; /* Integer user id */ |
| 228 | 238 | int isHuman; /* True if access by a human, not a spider or bot */ |
| @@ -800,29 +810,12 @@ | ||
| 800 | 810 | if( find_option("utc",0,0) ) g.fTimeFormat = 1; |
| 801 | 811 | if( find_option("localtime",0,0) ) g.fTimeFormat = 2; |
| 802 | 812 | if( zChdir && file_chdir(zChdir, 0) ){ |
| 803 | 813 | fossil_fatal("unable to change directories to %s", zChdir); |
| 804 | 814 | } |
| 805 | -#if defined(_WIN32) && USE_SEE | |
| 806 | - { | |
| 807 | - g.zPidKey = find_option("usepidkey",0,1); | |
| 808 | - if( g.zPidKey ){ | |
| 809 | - DWORD processId = 0; | |
| 810 | - LPVOID pAddress = NULL; | |
| 811 | - SIZE_T nSize = 0; | |
| 812 | - parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); | |
| 813 | - db_read_saved_encryption_key_from_process(processId, pAddress, nSize); | |
| 814 | - }else{ | |
| 815 | - const char *zSeeDbConfig = find_option("seedbcfg",0,1); | |
| 816 | - if( !zSeeDbConfig ){ | |
| 817 | - zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); | |
| 818 | - } | |
| 819 | - if( zSeeDbConfig ){ | |
| 820 | - db_read_saved_encryption_key_from_process_via_th1(zSeeDbConfig); | |
| 821 | - } | |
| 822 | - } | |
| 823 | - } | |
| 815 | +#if USE_SEE | |
| 816 | + db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ); | |
| 824 | 817 | #endif |
| 825 | 818 | if( find_option("help",0,0)!=0 ){ |
| 826 | 819 | /* If --help is found anywhere on the command line, translate the command |
| 827 | 820 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 828 | 821 | * does not begin with a "-" character. If all arguments start with "-", |
| @@ -1270,11 +1263,12 @@ | ||
| 1270 | 1263 | #endif |
| 1271 | 1264 | #if defined(USE_MMAN_H) |
| 1272 | 1265 | blob_append(pOut, "USE_MMAN_H\n", -1); |
| 1273 | 1266 | #endif |
| 1274 | 1267 | #if defined(USE_SEE) |
| 1275 | - blob_append(pOut, "USE_SEE\n", -1); | |
| 1268 | + blob_appendf(pOut, "USE_SEE (%s)\n", | |
| 1269 | + db_have_saved_encryption_key() ? "SET" : "UNSET"); | |
| 1276 | 1270 | #endif |
| 1277 | 1271 | #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) |
| 1278 | 1272 | blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n"); |
| 1279 | 1273 | #endif |
| 1280 | 1274 | |
| @@ -1716,10 +1710,12 @@ | ||
| 1716 | 1710 | #endif |
| 1717 | 1711 | /* If the repository has not been opened already, then find the |
| 1718 | 1712 | ** repository based on the first element of PATH_INFO and open it. |
| 1719 | 1713 | */ |
| 1720 | 1714 | if( !g.repositoryOpen ){ |
| 1715 | + char zBuf[24]; | |
| 1716 | + const char *zRepoExt = ".fossil"; | |
| 1721 | 1717 | char *zRepo; /* Candidate repository name */ |
| 1722 | 1718 | char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1723 | 1719 | const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1724 | 1720 | const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1725 | 1721 | char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| @@ -1737,11 +1733,11 @@ | ||
| 1737 | 1733 | size_t nBase = strlen(zBase); |
| 1738 | 1734 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1739 | 1735 | |
| 1740 | 1736 | /* The candidate repository name is some prefix of the PATH_INFO |
| 1741 | 1737 | ** with ".fossil" appended */ |
| 1742 | - zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); | |
| 1738 | + zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); | |
| 1743 | 1739 | if( g.fHttpTrace ){ |
| 1744 | 1740 | @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1745 | 1741 | fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1746 | 1742 | } |
| 1747 | 1743 | |
| @@ -1795,11 +1791,10 @@ | ||
| 1795 | 1791 | */ |
| 1796 | 1792 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1797 | 1793 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1798 | 1794 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1799 | 1795 | if( g.fHttpTrace ){ |
| 1800 | - char zBuf[24]; | |
| 1801 | 1796 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1802 | 1797 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1803 | 1798 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1804 | 1799 | } |
| 1805 | 1800 | } |
| @@ -1808,11 +1803,11 @@ | ||
| 1808 | 1803 | ** and check to see if there is a file or directory with the same |
| 1809 | 1804 | ** name as the raw PATH_INFO text. |
| 1810 | 1805 | */ |
| 1811 | 1806 | if( szFile<0 && i>0 ){ |
| 1812 | 1807 | const char *zMimetype; |
| 1813 | - assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); | |
| 1808 | + assert( file_is_repository_extension(&zRepo[j]) ); | |
| 1814 | 1809 | zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1815 | 1810 | |
| 1816 | 1811 | /* The PATH_INFO prefix seen so far is a valid directory. |
| 1817 | 1812 | ** Continue the loop with the next element of the PATH_INFO */ |
| 1818 | 1813 | if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){ |
| @@ -1833,11 +1828,11 @@ | ||
| 1833 | 1828 | ** pages. |
| 1834 | 1829 | */ |
| 1835 | 1830 | if( pFileGlob!=0 |
| 1836 | 1831 | && file_isfile(zCleanRepo, ExtFILE) |
| 1837 | 1832 | && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase)) |
| 1838 | - && sqlite3_strglob("*.fossil*",zRepo)!=0 | |
| 1833 | + && !file_contains_repository_extension(zRepo) | |
| 1839 | 1834 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1840 | 1835 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1841 | 1836 | ){ |
| 1842 | 1837 | Blob content; |
| 1843 | 1838 | blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); |
| @@ -1868,10 +1863,17 @@ | ||
| 1868 | 1863 | ** string is finished. Either zRepo contains the name of the |
| 1869 | 1864 | ** repository to be used, or else no repository could be found and |
| 1870 | 1865 | ** some kind of error response is required. |
| 1871 | 1866 | */ |
| 1872 | 1867 | if( szFile<1024 ){ |
| 1868 | +#if USE_SEE | |
| 1869 | + if( strcmp(zRepoExt,".fossil")==0 ){ | |
| 1870 | + fossil_free(zToFree); | |
| 1871 | + zRepoExt = ".efossil"; | |
| 1872 | + continue; | |
| 1873 | + } | |
| 1874 | +#endif | |
| 1873 | 1875 | set_base_url(0); |
| 1874 | 1876 | if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0) |
| 1875 | 1877 | && allowRepoList |
| 1876 | 1878 | && repo_list_page() ){ |
| 1877 | 1879 | /* Will return a list of repositories */ |
| @@ -1893,10 +1895,26 @@ | ||
| 1893 | 1895 | if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo); |
| 1894 | 1896 | if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1895 | 1897 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1896 | 1898 | zPathInfo += i; |
| 1897 | 1899 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1900 | +#if USE_SEE | |
| 1901 | + if( zPathInfo ){ | |
| 1902 | + if( g.fHttpTrace ){ | |
| 1903 | + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i); | |
| 1904 | + @ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) --> | |
| 1905 | + fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo); | |
| 1906 | + } | |
| 1907 | + if( strcmp(zPathInfo,"/setseekey")==0 | |
| 1908 | + && strcmp(zRepoExt,".efossil")==0 | |
| 1909 | + && !db_have_saved_encryption_key() ){ | |
| 1910 | + db_set_see_key_page(); | |
| 1911 | + cgi_reply(); | |
| 1912 | + fossil_exit(0); | |
| 1913 | + } | |
| 1914 | + } | |
| 1915 | +#endif | |
| 1898 | 1916 | db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1899 | 1917 | if( g.fHttpTrace ){ |
| 1900 | 1918 | @ <!-- repository: "%h(zRepo)" --> |
| 1901 | 1919 | @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> |
| 1902 | 1920 | @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> |
| @@ -2615,11 +2633,11 @@ | ||
| 2615 | 2633 | } |
| 2616 | 2634 | } |
| 2617 | 2635 | } |
| 2618 | 2636 | } |
| 2619 | 2637 | |
| 2620 | -#if defined(_WIN32) && USE_SEE | |
| 2638 | +#if USE_SEE | |
| 2621 | 2639 | /* |
| 2622 | 2640 | ** This function attempts to parse a string value in the following |
| 2623 | 2641 | ** format: |
| 2624 | 2642 | ** |
| 2625 | 2643 | ** "%lu:%p:%u" |
| @@ -2632,17 +2650,19 @@ | ||
| 2632 | 2650 | ** |
| 2633 | 2651 | ** If the specified value cannot be parsed, for any reason, a fatal |
| 2634 | 2652 | ** error will be raised and the process will be terminated. |
| 2635 | 2653 | */ |
| 2636 | 2654 | void parse_pid_key_value( |
| 2637 | - const char *zPidKey, /* The value to be parsed. */ | |
| 2638 | - DWORD *pProcessId, /* The extracted process identifier. */ | |
| 2639 | - LPVOID *ppAddress, /* The extracted pointer value. */ | |
| 2640 | - SIZE_T *pnSize /* The extracted size value. */ | |
| 2655 | + const char *zPidKey, /* The value to be parsed. */ | |
| 2656 | + PID_T *pProcessId, /* The extracted process identifier. */ | |
| 2657 | + LPVOID *ppAddress, /* The extracted pointer value. */ | |
| 2658 | + SIZE_T *pnSize /* The extracted size value. */ | |
| 2641 | 2659 | ){ |
| 2660 | + unsigned long processId = 0; | |
| 2642 | 2661 | unsigned int nSize = 0; |
| 2643 | - if( sscanf(zPidKey, "%lu:%p:%u", pProcessId, ppAddress, &nSize)==3 ){ | |
| 2662 | + if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){ | |
| 2663 | + *pProcessId = (PID_T)processId; | |
| 2644 | 2664 | *pnSize = (SIZE_T)nSize; |
| 2645 | 2665 | }else{ |
| 2646 | 2666 | fossil_fatal("failed to parse pid key"); |
| 2647 | 2667 | } |
| 2648 | 2668 | } |
| @@ -2656,25 +2676,26 @@ | ||
| 2656 | 2676 | ** Query parameters: |
| 2657 | 2677 | ** |
| 2658 | 2678 | ** usepidkey When present and available, also return the |
| 2659 | 2679 | ** address and size, within this server process, |
| 2660 | 2680 | ** of the saved database encryption key. This |
| 2661 | -** is only supported when using SEE on Windows. | |
| 2681 | +** is only supported when using SEE on Windows | |
| 2682 | +** or Linux. | |
| 2662 | 2683 | */ |
| 2663 | 2684 | void test_pid_page(void){ |
| 2664 | 2685 | login_check_credentials(); |
| 2665 | 2686 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 2666 | -#if defined(_WIN32) && USE_SEE | |
| 2687 | +#if USE_SEE | |
| 2667 | 2688 | if( P("usepidkey")!=0 ){ |
| 2668 | 2689 | if( g.zPidKey ){ |
| 2669 | 2690 | @ %s(g.zPidKey) |
| 2670 | 2691 | return; |
| 2671 | 2692 | }else{ |
| 2672 | 2693 | const char *zSavedKey = db_get_saved_encryption_key(); |
| 2673 | 2694 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 2674 | 2695 | if( zSavedKey!=0 && savedKeySize>0 ){ |
| 2675 | - @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize) | |
| 2696 | + @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize) | |
| 2676 | 2697 | return; |
| 2677 | 2698 | } |
| 2678 | 2699 | } |
| 2679 | 2700 | } |
| 2680 | 2701 | #endif |
| @@ -2770,11 +2791,11 @@ | ||
| 2770 | 2791 | ** --scgi Interpret input as SCGI rather than HTTP |
| 2771 | 2792 | ** --skin LABEL Use override skin LABEL. Use an empty string ("") |
| 2772 | 2793 | ** to force use of the current local skin config. |
| 2773 | 2794 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2774 | 2795 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2775 | -** only necessary when using SEE on Windows. | |
| 2796 | +** only necessary when using SEE on Windows or Linux. | |
| 2776 | 2797 | ** |
| 2777 | 2798 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2778 | 2799 | */ |
| 2779 | 2800 | void cmd_http(void){ |
| 2780 | 2801 | const char *zIpAddr = 0; |
| @@ -3111,11 +3132,11 @@ | ||
| 3111 | 3132 | ** --repolist If REPOSITORY is dir, URL "/" lists repos |
| 3112 | 3133 | ** --scgi Accept SCGI rather than HTTP |
| 3113 | 3134 | ** --skin LABEL Use override skin LABEL |
| 3114 | 3135 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3115 | 3136 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3116 | -** only necessary when using SEE on Windows. | |
| 3137 | +** only necessary when using SEE on Windows or Linux. | |
| 3117 | 3138 | ** |
| 3118 | 3139 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 3119 | 3140 | */ |
| 3120 | 3141 | void cmd_webserver(void){ |
| 3121 | 3142 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -3140,10 +3161,14 @@ | ||
| 3140 | 3161 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3141 | 3162 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3142 | 3163 | const char *zJsMode; /* The --jsmode parameter */ |
| 3143 | 3164 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3144 | 3165 | |
| 3166 | + | |
| 3167 | +#if USE_SEE | |
| 3168 | + db_setup_for_saved_encryption_key(); | |
| 3169 | +#endif | |
| 3145 | 3170 | |
| 3146 | 3171 | #if defined(_WIN32) |
| 3147 | 3172 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 3148 | 3173 | zStopperFile = find_option("stopper", 0, 1); |
| 3149 | 3174 | #endif |
| @@ -3356,10 +3381,11 @@ | ||
| 3356 | 3381 | ** This has to happen ahead of the other signal() calls below. |
| 3357 | 3382 | ** They apply after the HTTP hit is handled, but this one needs |
| 3358 | 3383 | ** to be registered while we're waiting for that to occur. |
| 3359 | 3384 | **/ |
| 3360 | 3385 | signal(SIGTERM, fossil_exit); |
| 3386 | + signal(SIGINT, fossil_exit); | |
| 3361 | 3387 | } |
| 3362 | 3388 | #endif /* !WIN32 */ |
| 3363 | 3389 | |
| 3364 | 3390 | /* Start up an HTTP server |
| 3365 | 3391 | */ |
| 3366 | 3392 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -24,10 +24,20 @@ | |
| 24 | # include <windows.h> |
| 25 | # include <io.h> |
| 26 | # define isatty(h) _isatty(h) |
| 27 | # define GETPID (int)GetCurrentProcessId |
| 28 | #endif |
| 29 | #include "main.h" |
| 30 | #include <string.h> |
| 31 | #include <time.h> |
| 32 | #include <fcntl.h> |
| 33 | #include <sys/types.h> |
| @@ -216,13 +226,13 @@ | |
| 216 | const char *zLogin; /* Login name. NULL or "" if not logged in. */ |
| 217 | const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ |
| 218 | const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ |
| 219 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 220 | ** SSL client identity */ |
| 221 | #if defined(_WIN32) && USE_SEE |
| 222 | const char *zPidKey; /* Saved value of the --usepidkey option. Only |
| 223 | * applicable when using SEE on Windows. */ |
| 224 | #endif |
| 225 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 226 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 227 | int userUid; /* Integer user id */ |
| 228 | int isHuman; /* True if access by a human, not a spider or bot */ |
| @@ -800,29 +810,12 @@ | |
| 800 | if( find_option("utc",0,0) ) g.fTimeFormat = 1; |
| 801 | if( find_option("localtime",0,0) ) g.fTimeFormat = 2; |
| 802 | if( zChdir && file_chdir(zChdir, 0) ){ |
| 803 | fossil_fatal("unable to change directories to %s", zChdir); |
| 804 | } |
| 805 | #if defined(_WIN32) && USE_SEE |
| 806 | { |
| 807 | g.zPidKey = find_option("usepidkey",0,1); |
| 808 | if( g.zPidKey ){ |
| 809 | DWORD processId = 0; |
| 810 | LPVOID pAddress = NULL; |
| 811 | SIZE_T nSize = 0; |
| 812 | parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize); |
| 813 | db_read_saved_encryption_key_from_process(processId, pAddress, nSize); |
| 814 | }else{ |
| 815 | const char *zSeeDbConfig = find_option("seedbcfg",0,1); |
| 816 | if( !zSeeDbConfig ){ |
| 817 | zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG"); |
| 818 | } |
| 819 | if( zSeeDbConfig ){ |
| 820 | db_read_saved_encryption_key_from_process_via_th1(zSeeDbConfig); |
| 821 | } |
| 822 | } |
| 823 | } |
| 824 | #endif |
| 825 | if( find_option("help",0,0)!=0 ){ |
| 826 | /* If --help is found anywhere on the command line, translate the command |
| 827 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 828 | * does not begin with a "-" character. If all arguments start with "-", |
| @@ -1270,11 +1263,12 @@ | |
| 1270 | #endif |
| 1271 | #if defined(USE_MMAN_H) |
| 1272 | blob_append(pOut, "USE_MMAN_H\n", -1); |
| 1273 | #endif |
| 1274 | #if defined(USE_SEE) |
| 1275 | blob_append(pOut, "USE_SEE\n", -1); |
| 1276 | #endif |
| 1277 | #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) |
| 1278 | blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n"); |
| 1279 | #endif |
| 1280 | |
| @@ -1716,10 +1710,12 @@ | |
| 1716 | #endif |
| 1717 | /* If the repository has not been opened already, then find the |
| 1718 | ** repository based on the first element of PATH_INFO and open it. |
| 1719 | */ |
| 1720 | if( !g.repositoryOpen ){ |
| 1721 | char *zRepo; /* Candidate repository name */ |
| 1722 | char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1723 | const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1724 | const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1725 | char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| @@ -1737,11 +1733,11 @@ | |
| 1737 | size_t nBase = strlen(zBase); |
| 1738 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1739 | |
| 1740 | /* The candidate repository name is some prefix of the PATH_INFO |
| 1741 | ** with ".fossil" appended */ |
| 1742 | zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); |
| 1743 | if( g.fHttpTrace ){ |
| 1744 | @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1745 | fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1746 | } |
| 1747 | |
| @@ -1795,11 +1791,10 @@ | |
| 1795 | */ |
| 1796 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1797 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1798 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1799 | if( g.fHttpTrace ){ |
| 1800 | char zBuf[24]; |
| 1801 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1802 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1803 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1804 | } |
| 1805 | } |
| @@ -1808,11 +1803,11 @@ | |
| 1808 | ** and check to see if there is a file or directory with the same |
| 1809 | ** name as the raw PATH_INFO text. |
| 1810 | */ |
| 1811 | if( szFile<0 && i>0 ){ |
| 1812 | const char *zMimetype; |
| 1813 | assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); |
| 1814 | zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1815 | |
| 1816 | /* The PATH_INFO prefix seen so far is a valid directory. |
| 1817 | ** Continue the loop with the next element of the PATH_INFO */ |
| 1818 | if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){ |
| @@ -1833,11 +1828,11 @@ | |
| 1833 | ** pages. |
| 1834 | */ |
| 1835 | if( pFileGlob!=0 |
| 1836 | && file_isfile(zCleanRepo, ExtFILE) |
| 1837 | && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase)) |
| 1838 | && sqlite3_strglob("*.fossil*",zRepo)!=0 |
| 1839 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1840 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1841 | ){ |
| 1842 | Blob content; |
| 1843 | blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); |
| @@ -1868,10 +1863,17 @@ | |
| 1868 | ** string is finished. Either zRepo contains the name of the |
| 1869 | ** repository to be used, or else no repository could be found and |
| 1870 | ** some kind of error response is required. |
| 1871 | */ |
| 1872 | if( szFile<1024 ){ |
| 1873 | set_base_url(0); |
| 1874 | if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0) |
| 1875 | && allowRepoList |
| 1876 | && repo_list_page() ){ |
| 1877 | /* Will return a list of repositories */ |
| @@ -1893,10 +1895,26 @@ | |
| 1893 | if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo); |
| 1894 | if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1895 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1896 | zPathInfo += i; |
| 1897 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1898 | db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1899 | if( g.fHttpTrace ){ |
| 1900 | @ <!-- repository: "%h(zRepo)" --> |
| 1901 | @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> |
| 1902 | @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> |
| @@ -2615,11 +2633,11 @@ | |
| 2615 | } |
| 2616 | } |
| 2617 | } |
| 2618 | } |
| 2619 | |
| 2620 | #if defined(_WIN32) && USE_SEE |
| 2621 | /* |
| 2622 | ** This function attempts to parse a string value in the following |
| 2623 | ** format: |
| 2624 | ** |
| 2625 | ** "%lu:%p:%u" |
| @@ -2632,17 +2650,19 @@ | |
| 2632 | ** |
| 2633 | ** If the specified value cannot be parsed, for any reason, a fatal |
| 2634 | ** error will be raised and the process will be terminated. |
| 2635 | */ |
| 2636 | void parse_pid_key_value( |
| 2637 | const char *zPidKey, /* The value to be parsed. */ |
| 2638 | DWORD *pProcessId, /* The extracted process identifier. */ |
| 2639 | LPVOID *ppAddress, /* The extracted pointer value. */ |
| 2640 | SIZE_T *pnSize /* The extracted size value. */ |
| 2641 | ){ |
| 2642 | unsigned int nSize = 0; |
| 2643 | if( sscanf(zPidKey, "%lu:%p:%u", pProcessId, ppAddress, &nSize)==3 ){ |
| 2644 | *pnSize = (SIZE_T)nSize; |
| 2645 | }else{ |
| 2646 | fossil_fatal("failed to parse pid key"); |
| 2647 | } |
| 2648 | } |
| @@ -2656,25 +2676,26 @@ | |
| 2656 | ** Query parameters: |
| 2657 | ** |
| 2658 | ** usepidkey When present and available, also return the |
| 2659 | ** address and size, within this server process, |
| 2660 | ** of the saved database encryption key. This |
| 2661 | ** is only supported when using SEE on Windows. |
| 2662 | */ |
| 2663 | void test_pid_page(void){ |
| 2664 | login_check_credentials(); |
| 2665 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 2666 | #if defined(_WIN32) && USE_SEE |
| 2667 | if( P("usepidkey")!=0 ){ |
| 2668 | if( g.zPidKey ){ |
| 2669 | @ %s(g.zPidKey) |
| 2670 | return; |
| 2671 | }else{ |
| 2672 | const char *zSavedKey = db_get_saved_encryption_key(); |
| 2673 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 2674 | if( zSavedKey!=0 && savedKeySize>0 ){ |
| 2675 | @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize) |
| 2676 | return; |
| 2677 | } |
| 2678 | } |
| 2679 | } |
| 2680 | #endif |
| @@ -2770,11 +2791,11 @@ | |
| 2770 | ** --scgi Interpret input as SCGI rather than HTTP |
| 2771 | ** --skin LABEL Use override skin LABEL. Use an empty string ("") |
| 2772 | ** to force use of the current local skin config. |
| 2773 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2774 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2775 | ** only necessary when using SEE on Windows. |
| 2776 | ** |
| 2777 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2778 | */ |
| 2779 | void cmd_http(void){ |
| 2780 | const char *zIpAddr = 0; |
| @@ -3111,11 +3132,11 @@ | |
| 3111 | ** --repolist If REPOSITORY is dir, URL "/" lists repos |
| 3112 | ** --scgi Accept SCGI rather than HTTP |
| 3113 | ** --skin LABEL Use override skin LABEL |
| 3114 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3115 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3116 | ** only necessary when using SEE on Windows. |
| 3117 | ** |
| 3118 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 3119 | */ |
| 3120 | void cmd_webserver(void){ |
| 3121 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -3140,10 +3161,14 @@ | |
| 3140 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3141 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3142 | const char *zJsMode; /* The --jsmode parameter */ |
| 3143 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3144 | |
| 3145 | |
| 3146 | #if defined(_WIN32) |
| 3147 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 3148 | zStopperFile = find_option("stopper", 0, 1); |
| 3149 | #endif |
| @@ -3356,10 +3381,11 @@ | |
| 3356 | ** This has to happen ahead of the other signal() calls below. |
| 3357 | ** They apply after the HTTP hit is handled, but this one needs |
| 3358 | ** to be registered while we're waiting for that to occur. |
| 3359 | **/ |
| 3360 | signal(SIGTERM, fossil_exit); |
| 3361 | } |
| 3362 | #endif /* !WIN32 */ |
| 3363 | |
| 3364 | /* Start up an HTTP server |
| 3365 | */ |
| 3366 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -24,10 +24,20 @@ | |
| 24 | # include <windows.h> |
| 25 | # include <io.h> |
| 26 | # define isatty(h) _isatty(h) |
| 27 | # define GETPID (int)GetCurrentProcessId |
| 28 | #endif |
| 29 | |
| 30 | /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ |
| 31 | #if USE_SEE |
| 32 | #if defined(_WIN32) |
| 33 | typedef DWORD PID_T; |
| 34 | #else |
| 35 | typedef pid_t PID_T; |
| 36 | #endif |
| 37 | #endif |
| 38 | |
| 39 | #include "main.h" |
| 40 | #include <string.h> |
| 41 | #include <time.h> |
| 42 | #include <fcntl.h> |
| 43 | #include <sys/types.h> |
| @@ -216,13 +226,13 @@ | |
| 226 | const char *zLogin; /* Login name. NULL or "" if not logged in. */ |
| 227 | const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ |
| 228 | const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ |
| 229 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 230 | ** SSL client identity */ |
| 231 | #if USE_SEE |
| 232 | const char *zPidKey; /* Saved value of the --usepidkey option. Only |
| 233 | * applicable when using SEE on Windows or Linux. */ |
| 234 | #endif |
| 235 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 236 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 237 | int userUid; /* Integer user id */ |
| 238 | int isHuman; /* True if access by a human, not a spider or bot */ |
| @@ -800,29 +810,12 @@ | |
| 810 | if( find_option("utc",0,0) ) g.fTimeFormat = 1; |
| 811 | if( find_option("localtime",0,0) ) g.fTimeFormat = 2; |
| 812 | if( zChdir && file_chdir(zChdir, 0) ){ |
| 813 | fossil_fatal("unable to change directories to %s", zChdir); |
| 814 | } |
| 815 | #if USE_SEE |
| 816 | db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ); |
| 817 | #endif |
| 818 | if( find_option("help",0,0)!=0 ){ |
| 819 | /* If --help is found anywhere on the command line, translate the command |
| 820 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 821 | * does not begin with a "-" character. If all arguments start with "-", |
| @@ -1270,11 +1263,12 @@ | |
| 1263 | #endif |
| 1264 | #if defined(USE_MMAN_H) |
| 1265 | blob_append(pOut, "USE_MMAN_H\n", -1); |
| 1266 | #endif |
| 1267 | #if defined(USE_SEE) |
| 1268 | blob_appendf(pOut, "USE_SEE (%s)\n", |
| 1269 | db_have_saved_encryption_key() ? "SET" : "UNSET"); |
| 1270 | #endif |
| 1271 | #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) |
| 1272 | blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n"); |
| 1273 | #endif |
| 1274 | |
| @@ -1716,10 +1710,12 @@ | |
| 1710 | #endif |
| 1711 | /* If the repository has not been opened already, then find the |
| 1712 | ** repository based on the first element of PATH_INFO and open it. |
| 1713 | */ |
| 1714 | if( !g.repositoryOpen ){ |
| 1715 | char zBuf[24]; |
| 1716 | const char *zRepoExt = ".fossil"; |
| 1717 | char *zRepo; /* Candidate repository name */ |
| 1718 | char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1719 | const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1720 | const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1721 | char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| @@ -1737,11 +1733,11 @@ | |
| 1733 | size_t nBase = strlen(zBase); |
| 1734 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1735 | |
| 1736 | /* The candidate repository name is some prefix of the PATH_INFO |
| 1737 | ** with ".fossil" appended */ |
| 1738 | zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); |
| 1739 | if( g.fHttpTrace ){ |
| 1740 | @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1741 | fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1742 | } |
| 1743 | |
| @@ -1795,11 +1791,10 @@ | |
| 1791 | */ |
| 1792 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1793 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1794 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1795 | if( g.fHttpTrace ){ |
| 1796 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1797 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1798 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1799 | } |
| 1800 | } |
| @@ -1808,11 +1803,11 @@ | |
| 1803 | ** and check to see if there is a file or directory with the same |
| 1804 | ** name as the raw PATH_INFO text. |
| 1805 | */ |
| 1806 | if( szFile<0 && i>0 ){ |
| 1807 | const char *zMimetype; |
| 1808 | assert( file_is_repository_extension(&zRepo[j]) ); |
| 1809 | zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1810 | |
| 1811 | /* The PATH_INFO prefix seen so far is a valid directory. |
| 1812 | ** Continue the loop with the next element of the PATH_INFO */ |
| 1813 | if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){ |
| @@ -1833,11 +1828,11 @@ | |
| 1828 | ** pages. |
| 1829 | */ |
| 1830 | if( pFileGlob!=0 |
| 1831 | && file_isfile(zCleanRepo, ExtFILE) |
| 1832 | && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase)) |
| 1833 | && !file_contains_repository_extension(zRepo) |
| 1834 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1835 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1836 | ){ |
| 1837 | Blob content; |
| 1838 | blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); |
| @@ -1868,10 +1863,17 @@ | |
| 1863 | ** string is finished. Either zRepo contains the name of the |
| 1864 | ** repository to be used, or else no repository could be found and |
| 1865 | ** some kind of error response is required. |
| 1866 | */ |
| 1867 | if( szFile<1024 ){ |
| 1868 | #if USE_SEE |
| 1869 | if( strcmp(zRepoExt,".fossil")==0 ){ |
| 1870 | fossil_free(zToFree); |
| 1871 | zRepoExt = ".efossil"; |
| 1872 | continue; |
| 1873 | } |
| 1874 | #endif |
| 1875 | set_base_url(0); |
| 1876 | if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0) |
| 1877 | && allowRepoList |
| 1878 | && repo_list_page() ){ |
| 1879 | /* Will return a list of repositories */ |
| @@ -1893,10 +1895,26 @@ | |
| 1895 | if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo); |
| 1896 | if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1897 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1898 | zPathInfo += i; |
| 1899 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1900 | #if USE_SEE |
| 1901 | if( zPathInfo ){ |
| 1902 | if( g.fHttpTrace ){ |
| 1903 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i); |
| 1904 | @ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) --> |
| 1905 | fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo); |
| 1906 | } |
| 1907 | if( strcmp(zPathInfo,"/setseekey")==0 |
| 1908 | && strcmp(zRepoExt,".efossil")==0 |
| 1909 | && !db_have_saved_encryption_key() ){ |
| 1910 | db_set_see_key_page(); |
| 1911 | cgi_reply(); |
| 1912 | fossil_exit(0); |
| 1913 | } |
| 1914 | } |
| 1915 | #endif |
| 1916 | db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1917 | if( g.fHttpTrace ){ |
| 1918 | @ <!-- repository: "%h(zRepo)" --> |
| 1919 | @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> |
| 1920 | @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> |
| @@ -2615,11 +2633,11 @@ | |
| 2633 | } |
| 2634 | } |
| 2635 | } |
| 2636 | } |
| 2637 | |
| 2638 | #if USE_SEE |
| 2639 | /* |
| 2640 | ** This function attempts to parse a string value in the following |
| 2641 | ** format: |
| 2642 | ** |
| 2643 | ** "%lu:%p:%u" |
| @@ -2632,17 +2650,19 @@ | |
| 2650 | ** |
| 2651 | ** If the specified value cannot be parsed, for any reason, a fatal |
| 2652 | ** error will be raised and the process will be terminated. |
| 2653 | */ |
| 2654 | void parse_pid_key_value( |
| 2655 | const char *zPidKey, /* The value to be parsed. */ |
| 2656 | PID_T *pProcessId, /* The extracted process identifier. */ |
| 2657 | LPVOID *ppAddress, /* The extracted pointer value. */ |
| 2658 | SIZE_T *pnSize /* The extracted size value. */ |
| 2659 | ){ |
| 2660 | unsigned long processId = 0; |
| 2661 | unsigned int nSize = 0; |
| 2662 | if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){ |
| 2663 | *pProcessId = (PID_T)processId; |
| 2664 | *pnSize = (SIZE_T)nSize; |
| 2665 | }else{ |
| 2666 | fossil_fatal("failed to parse pid key"); |
| 2667 | } |
| 2668 | } |
| @@ -2656,25 +2676,26 @@ | |
| 2676 | ** Query parameters: |
| 2677 | ** |
| 2678 | ** usepidkey When present and available, also return the |
| 2679 | ** address and size, within this server process, |
| 2680 | ** of the saved database encryption key. This |
| 2681 | ** is only supported when using SEE on Windows |
| 2682 | ** or Linux. |
| 2683 | */ |
| 2684 | void test_pid_page(void){ |
| 2685 | login_check_credentials(); |
| 2686 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 2687 | #if USE_SEE |
| 2688 | if( P("usepidkey")!=0 ){ |
| 2689 | if( g.zPidKey ){ |
| 2690 | @ %s(g.zPidKey) |
| 2691 | return; |
| 2692 | }else{ |
| 2693 | const char *zSavedKey = db_get_saved_encryption_key(); |
| 2694 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 2695 | if( zSavedKey!=0 && savedKeySize>0 ){ |
| 2696 | @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize) |
| 2697 | return; |
| 2698 | } |
| 2699 | } |
| 2700 | } |
| 2701 | #endif |
| @@ -2770,11 +2791,11 @@ | |
| 2791 | ** --scgi Interpret input as SCGI rather than HTTP |
| 2792 | ** --skin LABEL Use override skin LABEL. Use an empty string ("") |
| 2793 | ** to force use of the current local skin config. |
| 2794 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2795 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2796 | ** only necessary when using SEE on Windows or Linux. |
| 2797 | ** |
| 2798 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2799 | */ |
| 2800 | void cmd_http(void){ |
| 2801 | const char *zIpAddr = 0; |
| @@ -3111,11 +3132,11 @@ | |
| 3132 | ** --repolist If REPOSITORY is dir, URL "/" lists repos |
| 3133 | ** --scgi Accept SCGI rather than HTTP |
| 3134 | ** --skin LABEL Use override skin LABEL |
| 3135 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3136 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3137 | ** only necessary when using SEE on Windows or Linux. |
| 3138 | ** |
| 3139 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 3140 | */ |
| 3141 | void cmd_webserver(void){ |
| 3142 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -3140,10 +3161,14 @@ | |
| 3161 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3162 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3163 | const char *zJsMode; /* The --jsmode parameter */ |
| 3164 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3165 | |
| 3166 | |
| 3167 | #if USE_SEE |
| 3168 | db_setup_for_saved_encryption_key(); |
| 3169 | #endif |
| 3170 | |
| 3171 | #if defined(_WIN32) |
| 3172 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 3173 | zStopperFile = find_option("stopper", 0, 1); |
| 3174 | #endif |
| @@ -3356,10 +3381,11 @@ | |
| 3381 | ** This has to happen ahead of the other signal() calls below. |
| 3382 | ** They apply after the HTTP hit is handled, but this one needs |
| 3383 | ** to be registered while we're waiting for that to occur. |
| 3384 | **/ |
| 3385 | signal(SIGTERM, fossil_exit); |
| 3386 | signal(SIGINT, fossil_exit); |
| 3387 | } |
| 3388 | #endif /* !WIN32 */ |
| 3389 | |
| 3390 | /* Start up an HTTP server |
| 3391 | */ |
| 3392 |
+1
-1
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -2115,11 +2115,11 @@ | ||
| 2115 | 2115 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2116 | 2116 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2117 | 2117 | |
| 2118 | 2118 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 2119 | 2119 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2120 | - -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ | |
| 2120 | + -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore,stackAlloc \ | |
| 2121 | 2121 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ |
| 2122 | 2122 | -sENVIRONMENT=web \ |
| 2123 | 2123 | -sMODULARIZE \ |
| 2124 | 2124 | -sEXPORT_NAME=initPikchrModule \ |
| 2125 | 2125 | --minify 0 |
| 2126 | 2126 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -2115,11 +2115,11 @@ | |
| 2115 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2116 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2117 | |
| 2118 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 2119 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2120 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ |
| 2121 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ |
| 2122 | -sENVIRONMENT=web \ |
| 2123 | -sMODULARIZE \ |
| 2124 | -sEXPORT_NAME=initPikchrModule \ |
| 2125 | --minify 0 |
| 2126 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -2115,11 +2115,11 @@ | |
| 2115 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2116 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2117 | |
| 2118 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 2119 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2120 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore,stackAlloc \ |
| 2121 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ |
| 2122 | -sENVIRONMENT=web \ |
| 2123 | -sMODULARIZE \ |
| 2124 | -sEXPORT_NAME=initPikchrModule \ |
| 2125 | --minify 0 |
| 2126 |
+3
-3
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -230,11 +230,11 @@ | ||
| 230 | 230 | blob_appendf(ob, "</h%d>", level); |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | 233 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 234 | 234 | INTER_BLOCK(ob); |
| 235 | - blob_append_literal(ob, "<hr />\n"); | |
| 235 | + blob_append_literal(ob, "<hr>\n"); | |
| 236 | 236 | } |
| 237 | 237 | |
| 238 | 238 | |
| 239 | 239 | static void html_list( |
| 240 | 240 | struct Blob *ob, |
| @@ -758,16 +758,16 @@ | ||
| 758 | 758 | html_quote(ob, blob_buffer(alt), blob_size(alt)); |
| 759 | 759 | if( title && blob_size(title)>0 ){ |
| 760 | 760 | blob_append_literal(ob, "\" title=\""); |
| 761 | 761 | html_quote(ob, blob_buffer(title), blob_size(title)); |
| 762 | 762 | } |
| 763 | - blob_append_literal(ob, "\" />"); | |
| 763 | + blob_append_literal(ob, "\">"); | |
| 764 | 764 | return 1; |
| 765 | 765 | } |
| 766 | 766 | |
| 767 | 767 | static int html_linebreak(struct Blob *ob, void *opaque){ |
| 768 | - blob_append_literal(ob, "<br />\n"); | |
| 768 | + blob_append_literal(ob, "<br>\n"); | |
| 769 | 769 | return 1; |
| 770 | 770 | } |
| 771 | 771 | |
| 772 | 772 | static int html_link( |
| 773 | 773 | struct Blob *ob, |
| 774 | 774 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -230,11 +230,11 @@ | |
| 230 | blob_appendf(ob, "</h%d>", level); |
| 231 | } |
| 232 | |
| 233 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 234 | INTER_BLOCK(ob); |
| 235 | blob_append_literal(ob, "<hr />\n"); |
| 236 | } |
| 237 | |
| 238 | |
| 239 | static void html_list( |
| 240 | struct Blob *ob, |
| @@ -758,16 +758,16 @@ | |
| 758 | html_quote(ob, blob_buffer(alt), blob_size(alt)); |
| 759 | if( title && blob_size(title)>0 ){ |
| 760 | blob_append_literal(ob, "\" title=\""); |
| 761 | html_quote(ob, blob_buffer(title), blob_size(title)); |
| 762 | } |
| 763 | blob_append_literal(ob, "\" />"); |
| 764 | return 1; |
| 765 | } |
| 766 | |
| 767 | static int html_linebreak(struct Blob *ob, void *opaque){ |
| 768 | blob_append_literal(ob, "<br />\n"); |
| 769 | return 1; |
| 770 | } |
| 771 | |
| 772 | static int html_link( |
| 773 | struct Blob *ob, |
| 774 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -230,11 +230,11 @@ | |
| 230 | blob_appendf(ob, "</h%d>", level); |
| 231 | } |
| 232 | |
| 233 | static void html_hrule(struct Blob *ob, void *opaque){ |
| 234 | INTER_BLOCK(ob); |
| 235 | blob_append_literal(ob, "<hr>\n"); |
| 236 | } |
| 237 | |
| 238 | |
| 239 | static void html_list( |
| 240 | struct Blob *ob, |
| @@ -758,16 +758,16 @@ | |
| 758 | html_quote(ob, blob_buffer(alt), blob_size(alt)); |
| 759 | if( title && blob_size(title)>0 ){ |
| 760 | blob_append_literal(ob, "\" title=\""); |
| 761 | html_quote(ob, blob_buffer(title), blob_size(title)); |
| 762 | } |
| 763 | blob_append_literal(ob, "\">"); |
| 764 | return 1; |
| 765 | } |
| 766 | |
| 767 | static int html_linebreak(struct Blob *ob, void *opaque){ |
| 768 | blob_append_literal(ob, "<br>\n"); |
| 769 | return 1; |
| 770 | } |
| 771 | |
| 772 | static int html_link( |
| 773 | struct Blob *ob, |
| 774 |
+6
-19
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -762,28 +762,11 @@ | ||
| 762 | 762 | |
| 763 | 763 | /************************************************************************ |
| 764 | 764 | ** All of the information needed to do the merge is now contained in the |
| 765 | 765 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 766 | 766 | ** |
| 767 | - ** First, find files in M and V but not in P and report conflicts. | |
| 768 | - ** The file in M will be ignored. It will be treated as if it | |
| 769 | - ** does not exist. | |
| 770 | - */ | |
| 771 | - db_prepare(&q, | |
| 772 | - "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" | |
| 773 | - ); | |
| 774 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 775 | - int idm = db_column_int(&q, 0); | |
| 776 | - char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); | |
| 777 | - fossil_warning("WARNING: no common ancestor for %s", zName); | |
| 778 | - free(zName); | |
| 779 | - db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); | |
| 780 | - } | |
| 781 | - db_finalize(&q); | |
| 782 | - | |
| 783 | - /* | |
| 784 | - ** Find files that have changed from P->M but not P->V. | |
| 767 | + ** First, find files that have changed from P->M but not P->V. | |
| 785 | 768 | ** Copy the M content over into V. |
| 786 | 769 | */ |
| 787 | 770 | db_prepare(&q, |
| 788 | 771 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 789 | 772 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -809,14 +792,18 @@ | ||
| 809 | 792 | } |
| 810 | 793 | db_finalize(&q); |
| 811 | 794 | |
| 812 | 795 | /* |
| 813 | 796 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| 797 | + ** | |
| 798 | + ** Proceed even if the file doesn't exist on P, just like the common ancestor | |
| 799 | + ** of M and V is an empty file. In this case, merge conflict marks will be | |
| 800 | + ** added to the file and user will be forced to take a decision. | |
| 814 | 801 | */ |
| 815 | 802 | db_prepare(&q, |
| 816 | 803 | "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" |
| 817 | - " WHERE idp>0 AND idv>0 AND idm>0" | |
| 804 | + " WHERE idv>0 AND idm>0" | |
| 818 | 805 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 819 | 806 | glob_expr("fv.fn", zBinGlob) |
| 820 | 807 | ); |
| 821 | 808 | while( db_step(&q)==SQLITE_ROW ){ |
| 822 | 809 | int ridm = db_column_int(&q, 0); |
| 823 | 810 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -762,28 +762,11 @@ | |
| 762 | |
| 763 | /************************************************************************ |
| 764 | ** All of the information needed to do the merge is now contained in the |
| 765 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 766 | ** |
| 767 | ** First, find files in M and V but not in P and report conflicts. |
| 768 | ** The file in M will be ignored. It will be treated as if it |
| 769 | ** does not exist. |
| 770 | */ |
| 771 | db_prepare(&q, |
| 772 | "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0" |
| 773 | ); |
| 774 | while( db_step(&q)==SQLITE_ROW ){ |
| 775 | int idm = db_column_int(&q, 0); |
| 776 | char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm); |
| 777 | fossil_warning("WARNING: no common ancestor for %s", zName); |
| 778 | free(zName); |
| 779 | db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm); |
| 780 | } |
| 781 | db_finalize(&q); |
| 782 | |
| 783 | /* |
| 784 | ** Find files that have changed from P->M but not P->V. |
| 785 | ** Copy the M content over into V. |
| 786 | */ |
| 787 | db_prepare(&q, |
| 788 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 789 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -809,14 +792,18 @@ | |
| 809 | } |
| 810 | db_finalize(&q); |
| 811 | |
| 812 | /* |
| 813 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| 814 | */ |
| 815 | db_prepare(&q, |
| 816 | "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" |
| 817 | " WHERE idp>0 AND idv>0 AND idm>0" |
| 818 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 819 | glob_expr("fv.fn", zBinGlob) |
| 820 | ); |
| 821 | while( db_step(&q)==SQLITE_ROW ){ |
| 822 | int ridm = db_column_int(&q, 0); |
| 823 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -762,28 +762,11 @@ | |
| 762 | |
| 763 | /************************************************************************ |
| 764 | ** All of the information needed to do the merge is now contained in the |
| 765 | ** FV table. Starting here, we begin to actually carry out the merge. |
| 766 | ** |
| 767 | ** First, find files that have changed from P->M but not P->V. |
| 768 | ** Copy the M content over into V. |
| 769 | */ |
| 770 | db_prepare(&q, |
| 771 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 772 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -809,14 +792,18 @@ | |
| 792 | } |
| 793 | db_finalize(&q); |
| 794 | |
| 795 | /* |
| 796 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| 797 | ** |
| 798 | ** Proceed even if the file doesn't exist on P, just like the common ancestor |
| 799 | ** of M and V is an empty file. In this case, merge conflict marks will be |
| 800 | ** added to the file and user will be forced to take a decision. |
| 801 | */ |
| 802 | db_prepare(&q, |
| 803 | "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv" |
| 804 | " WHERE idv>0 AND idm>0" |
| 805 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 806 | glob_expr("fv.fn", zBinGlob) |
| 807 | ); |
| 808 | while( db_step(&q)==SQLITE_ROW ){ |
| 809 | int ridm = db_column_int(&q, 0); |
| 810 |
+6
-4
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -861,13 +861,15 @@ | ||
| 861 | 861 | ** are reverted and permanently lost. |
| 862 | 862 | ** -n|--dry-run Do nothing, but print what would have happened |
| 863 | 863 | ** -v|--verbose Extra output explaining what happens |
| 864 | 864 | ** |
| 865 | 865 | ** > fossil patch diff [DIRECTORY] FILENAME |
| 866 | +** > fossil patch gdiff [DIRECTORY] FILENAME | |
| 866 | 867 | ** |
| 867 | 868 | ** Show a human-readable diff for the patch. All the usual |
| 868 | -** diff flags described at "fossil help diff" apply. In addition: | |
| 869 | +** diff flags described at "fossil help diff" apply. With gdiff, | |
| 870 | +** gdiff-command is used instead of internal diff logic. In addition: | |
| 869 | 871 | ** |
| 870 | 872 | ** -f|--force Continue trying to perform the diff even if |
| 871 | 873 | ** baseline information is missing from the current |
| 872 | 874 | ** repository |
| 873 | 875 | ** |
| @@ -908,11 +910,11 @@ | ||
| 908 | 910 | void patch_cmd(void){ |
| 909 | 911 | const char *zCmd; |
| 910 | 912 | size_t n; |
| 911 | 913 | if( g.argc<3 ){ |
| 912 | 914 | patch_usage: |
| 913 | - usage("apply|create|diff|pull|push|view"); | |
| 915 | + usage("apply|create|diff|gdiff|pull|push|view"); | |
| 914 | 916 | } |
| 915 | 917 | zCmd = g.argv[2]; |
| 916 | 918 | n = strlen(zCmd); |
| 917 | 919 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 918 | 920 | char *zIn; |
| @@ -934,23 +936,23 @@ | ||
| 934 | 936 | verify_all_options(); |
| 935 | 937 | db_must_be_within_tree(); |
| 936 | 938 | patch_create(flags, zOut, stdout); |
| 937 | 939 | fossil_free(zOut); |
| 938 | 940 | }else |
| 939 | - if( strncmp(zCmd, "diff", n)==0 ){ | |
| 941 | + if( (strncmp(zCmd, "diff", n)==0) || (strncmp(zCmd, "gdiff", n)==0) ){ | |
| 940 | 942 | char *zIn; |
| 941 | 943 | unsigned flags = 0; |
| 942 | 944 | DiffConfig DCfg; |
| 943 | 945 | |
| 944 | 946 | if( find_option("tk",0,0)!=0 ){ |
| 945 | 947 | db_close(0); |
| 946 | 948 | diff_tk("patch diff", 3); |
| 947 | 949 | return; |
| 948 | 950 | } |
| 949 | - diff_options(&DCfg, zCmd[0]=='g', 0); | |
| 950 | 951 | db_find_and_open_repository(0, 0); |
| 951 | 952 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 953 | + diff_options(&DCfg, zCmd[0]=='g', 0); | |
| 952 | 954 | verify_all_options(); |
| 953 | 955 | zIn = patch_find_patch_filename("apply"); |
| 954 | 956 | patch_attach(zIn, stdin); |
| 955 | 957 | patch_diff(flags, &DCfg); |
| 956 | 958 | fossil_free(zIn); |
| 957 | 959 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -861,13 +861,15 @@ | |
| 861 | ** are reverted and permanently lost. |
| 862 | ** -n|--dry-run Do nothing, but print what would have happened |
| 863 | ** -v|--verbose Extra output explaining what happens |
| 864 | ** |
| 865 | ** > fossil patch diff [DIRECTORY] FILENAME |
| 866 | ** |
| 867 | ** Show a human-readable diff for the patch. All the usual |
| 868 | ** diff flags described at "fossil help diff" apply. In addition: |
| 869 | ** |
| 870 | ** -f|--force Continue trying to perform the diff even if |
| 871 | ** baseline information is missing from the current |
| 872 | ** repository |
| 873 | ** |
| @@ -908,11 +910,11 @@ | |
| 908 | void patch_cmd(void){ |
| 909 | const char *zCmd; |
| 910 | size_t n; |
| 911 | if( g.argc<3 ){ |
| 912 | patch_usage: |
| 913 | usage("apply|create|diff|pull|push|view"); |
| 914 | } |
| 915 | zCmd = g.argv[2]; |
| 916 | n = strlen(zCmd); |
| 917 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 918 | char *zIn; |
| @@ -934,23 +936,23 @@ | |
| 934 | verify_all_options(); |
| 935 | db_must_be_within_tree(); |
| 936 | patch_create(flags, zOut, stdout); |
| 937 | fossil_free(zOut); |
| 938 | }else |
| 939 | if( strncmp(zCmd, "diff", n)==0 ){ |
| 940 | char *zIn; |
| 941 | unsigned flags = 0; |
| 942 | DiffConfig DCfg; |
| 943 | |
| 944 | if( find_option("tk",0,0)!=0 ){ |
| 945 | db_close(0); |
| 946 | diff_tk("patch diff", 3); |
| 947 | return; |
| 948 | } |
| 949 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 950 | db_find_and_open_repository(0, 0); |
| 951 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 952 | verify_all_options(); |
| 953 | zIn = patch_find_patch_filename("apply"); |
| 954 | patch_attach(zIn, stdin); |
| 955 | patch_diff(flags, &DCfg); |
| 956 | fossil_free(zIn); |
| 957 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -861,13 +861,15 @@ | |
| 861 | ** are reverted and permanently lost. |
| 862 | ** -n|--dry-run Do nothing, but print what would have happened |
| 863 | ** -v|--verbose Extra output explaining what happens |
| 864 | ** |
| 865 | ** > fossil patch diff [DIRECTORY] FILENAME |
| 866 | ** > fossil patch gdiff [DIRECTORY] FILENAME |
| 867 | ** |
| 868 | ** Show a human-readable diff for the patch. All the usual |
| 869 | ** diff flags described at "fossil help diff" apply. With gdiff, |
| 870 | ** gdiff-command is used instead of internal diff logic. In addition: |
| 871 | ** |
| 872 | ** -f|--force Continue trying to perform the diff even if |
| 873 | ** baseline information is missing from the current |
| 874 | ** repository |
| 875 | ** |
| @@ -908,11 +910,11 @@ | |
| 910 | void patch_cmd(void){ |
| 911 | const char *zCmd; |
| 912 | size_t n; |
| 913 | if( g.argc<3 ){ |
| 914 | patch_usage: |
| 915 | usage("apply|create|diff|gdiff|pull|push|view"); |
| 916 | } |
| 917 | zCmd = g.argv[2]; |
| 918 | n = strlen(zCmd); |
| 919 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 920 | char *zIn; |
| @@ -934,23 +936,23 @@ | |
| 936 | verify_all_options(); |
| 937 | db_must_be_within_tree(); |
| 938 | patch_create(flags, zOut, stdout); |
| 939 | fossil_free(zOut); |
| 940 | }else |
| 941 | if( (strncmp(zCmd, "diff", n)==0) || (strncmp(zCmd, "gdiff", n)==0) ){ |
| 942 | char *zIn; |
| 943 | unsigned flags = 0; |
| 944 | DiffConfig DCfg; |
| 945 | |
| 946 | if( find_option("tk",0,0)!=0 ){ |
| 947 | db_close(0); |
| 948 | diff_tk("patch diff", 3); |
| 949 | return; |
| 950 | } |
| 951 | db_find_and_open_repository(0, 0); |
| 952 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 953 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 954 | verify_all_options(); |
| 955 | zIn = patch_find_patch_filename("apply"); |
| 956 | patch_attach(zIn, stdin); |
| 957 | patch_diff(flags, &DCfg); |
| 958 | fossil_free(zIn); |
| 959 |
+4
-4
| --- src/piechart.c | ||
| +++ src/piechart.c | ||
| @@ -312,17 +312,17 @@ | ||
| 312 | 312 | db_finalize(&ins); |
| 313 | 313 | if( n>1 ){ |
| 314 | 314 | @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;"> |
| 315 | 315 | piechart_render(width,height, PIE_OTHER|PIE_PERCENT); |
| 316 | 316 | @ </svg> |
| 317 | - @ <hr /> | |
| 317 | + @ <hr> | |
| 318 | 318 | } |
| 319 | 319 | @ <form method="POST" action='%R/test-piechart'> |
| 320 | - @ <p>Comma-separated list of slice widths:<br /> | |
| 321 | - @ <input type='text' name='data' size='80' value='%h(zData)'/><br /> | |
| 320 | + @ <p>Comma-separated list of slice widths:<br> | |
| 321 | + @ <input type='text' name='data' size='80' value='%h(zData)'/><br> | |
| 322 | 322 | @ Width: <input type='text' size='8' name='width' value='%d(width)'/> |
| 323 | - @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br /> | |
| 323 | + @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br> | |
| 324 | 324 | @ <input type='submit' value='Draw The Pie Chart'/> |
| 325 | 325 | @ </form> |
| 326 | 326 | @ <p>Interesting test cases: |
| 327 | 327 | @ <ul> |
| 328 | 328 | @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a> |
| 329 | 329 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -312,17 +312,17 @@ | |
| 312 | db_finalize(&ins); |
| 313 | if( n>1 ){ |
| 314 | @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;"> |
| 315 | piechart_render(width,height, PIE_OTHER|PIE_PERCENT); |
| 316 | @ </svg> |
| 317 | @ <hr /> |
| 318 | } |
| 319 | @ <form method="POST" action='%R/test-piechart'> |
| 320 | @ <p>Comma-separated list of slice widths:<br /> |
| 321 | @ <input type='text' name='data' size='80' value='%h(zData)'/><br /> |
| 322 | @ Width: <input type='text' size='8' name='width' value='%d(width)'/> |
| 323 | @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br /> |
| 324 | @ <input type='submit' value='Draw The Pie Chart'/> |
| 325 | @ </form> |
| 326 | @ <p>Interesting test cases: |
| 327 | @ <ul> |
| 328 | @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a> |
| 329 |
| --- src/piechart.c | |
| +++ src/piechart.c | |
| @@ -312,17 +312,17 @@ | |
| 312 | db_finalize(&ins); |
| 313 | if( n>1 ){ |
| 314 | @ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;"> |
| 315 | piechart_render(width,height, PIE_OTHER|PIE_PERCENT); |
| 316 | @ </svg> |
| 317 | @ <hr> |
| 318 | } |
| 319 | @ <form method="POST" action='%R/test-piechart'> |
| 320 | @ <p>Comma-separated list of slice widths:<br> |
| 321 | @ <input type='text' name='data' size='80' value='%h(zData)'/><br> |
| 322 | @ Width: <input type='text' size='8' name='width' value='%d(width)'/> |
| 323 | @ Height: <input type='text' size='8' name='height' value='%d(height)'/><br> |
| 324 | @ <input type='submit' value='Draw The Pie Chart'/> |
| 325 | @ </form> |
| 326 | @ <p>Interesting test cases: |
| 327 | @ <ul> |
| 328 | @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a> |
| 329 |
+29
-9
| --- src/repolist.c | ||
| +++ src/repolist.c | ||
| @@ -143,11 +143,15 @@ | ||
| 143 | 143 | blob_init(&base, g.zRepositoryName, -1); |
| 144 | 144 | sqlite3_open(":memory:", &g.db); |
| 145 | 145 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 146 | 146 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 147 | 147 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 148 | - db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); | |
| 148 | + db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" | |
| 149 | +#if USE_SEE | |
| 150 | + " AND pathname NOT GLOB '*[^/].efossil'" | |
| 151 | +#endif | |
| 152 | + ); | |
| 149 | 153 | allRepo = 0; |
| 150 | 154 | } |
| 151 | 155 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 152 | 156 | if( n==0 ){ |
| 153 | 157 | sqlite3_close(g.db); |
| @@ -170,17 +174,22 @@ | ||
| 170 | 174 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 171 | 175 | rNow = db_double(0, "SELECT julianday('now')"); |
| 172 | 176 | while( db_step(&q)==SQLITE_ROW ){ |
| 173 | 177 | const char *zName = db_column_text(&q, 0); |
| 174 | 178 | int nName = (int)strlen(zName); |
| 179 | + int nSuffix = 7; /* ".fossil" */ | |
| 175 | 180 | char *zUrl; |
| 176 | 181 | char *zAge; |
| 177 | 182 | char *zFull; |
| 178 | 183 | RepoInfo x; |
| 179 | 184 | sqlite3_int64 iAge; |
| 180 | - if( nName<7 ) continue; | |
| 181 | - zUrl = sqlite3_mprintf("%.*s", nName-7, zName); | |
| 185 | +#if USE_SEE | |
| 186 | + int bEncrypted = sqlite3_strglob("*.efossil", zName)==0; | |
| 187 | + if( bEncrypted ) nSuffix = 8; /* ".efossil" */ | |
| 188 | +#endif | |
| 189 | + if( nName<nSuffix ) continue; | |
| 190 | + zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName); | |
| 182 | 191 | if( zName[0]=='/' |
| 183 | 192 | #ifdef _WIN32 |
| 184 | 193 | || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 |
| 185 | 194 | #endif |
| 186 | 195 | ){ |
| @@ -197,11 +206,15 @@ | ||
| 197 | 206 | zSkinRepo = mprintf("%s", x.zRepoName); |
| 198 | 207 | zSkinUrl = mprintf("%s", zUrl); |
| 199 | 208 | } |
| 200 | 209 | } |
| 201 | 210 | fossil_free(zFull); |
| 202 | - if( !x.isValid ){ | |
| 211 | + if( !x.isValid | |
| 212 | +#if USE_SEE | |
| 213 | + && !bEncrypted | |
| 214 | +#endif | |
| 215 | + ){ | |
| 203 | 216 | continue; |
| 204 | 217 | } |
| 205 | 218 | if( x.isRepolistSkin==2 && !allRepo ){ |
| 206 | 219 | /* Repositories with repolist-skin==2 are omitted from directory |
| 207 | 220 | ** scan lists, but included in "fossil all ui" lists */ |
| @@ -218,11 +231,11 @@ | ||
| 218 | 231 | /* This repository has no entry in the "event" table. |
| 219 | 232 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 220 | 233 | zAge = mprintf("unknown"); |
| 221 | 234 | } |
| 222 | 235 | blob_append_sql(&html, "<tr><td valign='top'>"); |
| 223 | - if( sqlite3_strglob("*.fossil", zName)!=0 ){ | |
| 236 | + if( !file_ends_with_repository_extension(zName,0) ){ | |
| 224 | 237 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 225 | 238 | ** do not work for repositories whose names do not end in ".fossil". |
| 226 | 239 | ** So do not hyperlink those cases. */ |
| 227 | 240 | blob_append_sql(&html,"%h",zName); |
| 228 | 241 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| @@ -230,21 +243,28 @@ | ||
| 230 | 243 | blob_append_sql(&html, "%h (hidden)", zName); |
| 231 | 244 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 232 | 245 | blob_append_sql(&html, |
| 233 | 246 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 234 | 247 | zUrl, zName); |
| 235 | - }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){ | |
| 248 | + }else if( file_ends_with_repository_extension(zName,1) ){ | |
| 236 | 249 | /* As described in |
| 237 | 250 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| 238 | 251 | ** foo.fossil and foo/bar.fossil both exist and we create a |
| 239 | 252 | ** link to foo/bar/... then the URI dispatcher will instead |
| 240 | 253 | ** see that as a link to foo.fossil. In such cases, do not |
| 241 | 254 | ** emit a link to foo/bar.fossil. */ |
| 242 | 255 | char * zDirPart = file_dirname(zName); |
| 243 | 256 | if( db_exists("SELECT 1 FROM sfile " |
| 244 | - "WHERE pathname=(%Q || '.fossil') COLLATE nocase", | |
| 245 | - zDirPart) ){ | |
| 257 | + "WHERE pathname=(%Q || '.fossil') COLLATE nocase" | |
| 258 | +#if USE_SEE | |
| 259 | + " OR pathname=(%Q || '.efossil') COLLATE nocase" | |
| 260 | +#endif | |
| 261 | + , zDirPart | |
| 262 | +#if USE_SEE | |
| 263 | + , zDirPart | |
| 264 | +#endif | |
| 265 | + ) ){ | |
| 246 | 266 | blob_append_sql(&html, |
| 247 | 267 | "<s>%h</s> (directory/repo name collision)\n", |
| 248 | 268 | zName); |
| 249 | 269 | }else{ |
| 250 | 270 | blob_append_sql(&html, |
| @@ -300,11 +320,11 @@ | ||
| 300 | 320 | }else{ |
| 301 | 321 | /* If no repositories were found that had the "repolist_skin" |
| 302 | 322 | ** property set, then use a default skin */ |
| 303 | 323 | @ <html> |
| 304 | 324 | @ <head> |
| 305 | - @ <base href="%s(g.zBaseURL)/" /> | |
| 325 | + @ <base href="%s(g.zBaseURL)/"> | |
| 306 | 326 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 307 | 327 | @ <title>Repository List</title> |
| 308 | 328 | @ </head> |
| 309 | 329 | @ <body> |
| 310 | 330 | @ <h1 align="center">Fossil Repositories</h1> |
| 311 | 331 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -143,11 +143,15 @@ | |
| 143 | blob_init(&base, g.zRepositoryName, -1); |
| 144 | sqlite3_open(":memory:", &g.db); |
| 145 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 146 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 147 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 148 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 149 | allRepo = 0; |
| 150 | } |
| 151 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 152 | if( n==0 ){ |
| 153 | sqlite3_close(g.db); |
| @@ -170,17 +174,22 @@ | |
| 170 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 171 | rNow = db_double(0, "SELECT julianday('now')"); |
| 172 | while( db_step(&q)==SQLITE_ROW ){ |
| 173 | const char *zName = db_column_text(&q, 0); |
| 174 | int nName = (int)strlen(zName); |
| 175 | char *zUrl; |
| 176 | char *zAge; |
| 177 | char *zFull; |
| 178 | RepoInfo x; |
| 179 | sqlite3_int64 iAge; |
| 180 | if( nName<7 ) continue; |
| 181 | zUrl = sqlite3_mprintf("%.*s", nName-7, zName); |
| 182 | if( zName[0]=='/' |
| 183 | #ifdef _WIN32 |
| 184 | || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 |
| 185 | #endif |
| 186 | ){ |
| @@ -197,11 +206,15 @@ | |
| 197 | zSkinRepo = mprintf("%s", x.zRepoName); |
| 198 | zSkinUrl = mprintf("%s", zUrl); |
| 199 | } |
| 200 | } |
| 201 | fossil_free(zFull); |
| 202 | if( !x.isValid ){ |
| 203 | continue; |
| 204 | } |
| 205 | if( x.isRepolistSkin==2 && !allRepo ){ |
| 206 | /* Repositories with repolist-skin==2 are omitted from directory |
| 207 | ** scan lists, but included in "fossil all ui" lists */ |
| @@ -218,11 +231,11 @@ | |
| 218 | /* This repository has no entry in the "event" table. |
| 219 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 220 | zAge = mprintf("unknown"); |
| 221 | } |
| 222 | blob_append_sql(&html, "<tr><td valign='top'>"); |
| 223 | if( sqlite3_strglob("*.fossil", zName)!=0 ){ |
| 224 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 225 | ** do not work for repositories whose names do not end in ".fossil". |
| 226 | ** So do not hyperlink those cases. */ |
| 227 | blob_append_sql(&html,"%h",zName); |
| 228 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| @@ -230,21 +243,28 @@ | |
| 230 | blob_append_sql(&html, "%h (hidden)", zName); |
| 231 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 232 | blob_append_sql(&html, |
| 233 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 234 | zUrl, zName); |
| 235 | }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){ |
| 236 | /* As described in |
| 237 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| 238 | ** foo.fossil and foo/bar.fossil both exist and we create a |
| 239 | ** link to foo/bar/... then the URI dispatcher will instead |
| 240 | ** see that as a link to foo.fossil. In such cases, do not |
| 241 | ** emit a link to foo/bar.fossil. */ |
| 242 | char * zDirPart = file_dirname(zName); |
| 243 | if( db_exists("SELECT 1 FROM sfile " |
| 244 | "WHERE pathname=(%Q || '.fossil') COLLATE nocase", |
| 245 | zDirPart) ){ |
| 246 | blob_append_sql(&html, |
| 247 | "<s>%h</s> (directory/repo name collision)\n", |
| 248 | zName); |
| 249 | }else{ |
| 250 | blob_append_sql(&html, |
| @@ -300,11 +320,11 @@ | |
| 300 | }else{ |
| 301 | /* If no repositories were found that had the "repolist_skin" |
| 302 | ** property set, then use a default skin */ |
| 303 | @ <html> |
| 304 | @ <head> |
| 305 | @ <base href="%s(g.zBaseURL)/" /> |
| 306 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 307 | @ <title>Repository List</title> |
| 308 | @ </head> |
| 309 | @ <body> |
| 310 | @ <h1 align="center">Fossil Repositories</h1> |
| 311 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -143,11 +143,15 @@ | |
| 143 | blob_init(&base, g.zRepositoryName, -1); |
| 144 | sqlite3_open(":memory:", &g.db); |
| 145 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 146 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 147 | vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE); |
| 148 | db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'" |
| 149 | #if USE_SEE |
| 150 | " AND pathname NOT GLOB '*[^/].efossil'" |
| 151 | #endif |
| 152 | ); |
| 153 | allRepo = 0; |
| 154 | } |
| 155 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 156 | if( n==0 ){ |
| 157 | sqlite3_close(g.db); |
| @@ -170,17 +174,22 @@ | |
| 174 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 175 | rNow = db_double(0, "SELECT julianday('now')"); |
| 176 | while( db_step(&q)==SQLITE_ROW ){ |
| 177 | const char *zName = db_column_text(&q, 0); |
| 178 | int nName = (int)strlen(zName); |
| 179 | int nSuffix = 7; /* ".fossil" */ |
| 180 | char *zUrl; |
| 181 | char *zAge; |
| 182 | char *zFull; |
| 183 | RepoInfo x; |
| 184 | sqlite3_int64 iAge; |
| 185 | #if USE_SEE |
| 186 | int bEncrypted = sqlite3_strglob("*.efossil", zName)==0; |
| 187 | if( bEncrypted ) nSuffix = 8; /* ".efossil" */ |
| 188 | #endif |
| 189 | if( nName<nSuffix ) continue; |
| 190 | zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName); |
| 191 | if( zName[0]=='/' |
| 192 | #ifdef _WIN32 |
| 193 | || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 |
| 194 | #endif |
| 195 | ){ |
| @@ -197,11 +206,15 @@ | |
| 206 | zSkinRepo = mprintf("%s", x.zRepoName); |
| 207 | zSkinUrl = mprintf("%s", zUrl); |
| 208 | } |
| 209 | } |
| 210 | fossil_free(zFull); |
| 211 | if( !x.isValid |
| 212 | #if USE_SEE |
| 213 | && !bEncrypted |
| 214 | #endif |
| 215 | ){ |
| 216 | continue; |
| 217 | } |
| 218 | if( x.isRepolistSkin==2 && !allRepo ){ |
| 219 | /* Repositories with repolist-skin==2 are omitted from directory |
| 220 | ** scan lists, but included in "fossil all ui" lists */ |
| @@ -218,11 +231,11 @@ | |
| 231 | /* This repository has no entry in the "event" table. |
| 232 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 233 | zAge = mprintf("unknown"); |
| 234 | } |
| 235 | blob_append_sql(&html, "<tr><td valign='top'>"); |
| 236 | if( !file_ends_with_repository_extension(zName,0) ){ |
| 237 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 238 | ** do not work for repositories whose names do not end in ".fossil". |
| 239 | ** So do not hyperlink those cases. */ |
| 240 | blob_append_sql(&html,"%h",zName); |
| 241 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| @@ -230,21 +243,28 @@ | |
| 243 | blob_append_sql(&html, "%h (hidden)", zName); |
| 244 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 245 | blob_append_sql(&html, |
| 246 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 247 | zUrl, zName); |
| 248 | }else if( file_ends_with_repository_extension(zName,1) ){ |
| 249 | /* As described in |
| 250 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| 251 | ** foo.fossil and foo/bar.fossil both exist and we create a |
| 252 | ** link to foo/bar/... then the URI dispatcher will instead |
| 253 | ** see that as a link to foo.fossil. In such cases, do not |
| 254 | ** emit a link to foo/bar.fossil. */ |
| 255 | char * zDirPart = file_dirname(zName); |
| 256 | if( db_exists("SELECT 1 FROM sfile " |
| 257 | "WHERE pathname=(%Q || '.fossil') COLLATE nocase" |
| 258 | #if USE_SEE |
| 259 | " OR pathname=(%Q || '.efossil') COLLATE nocase" |
| 260 | #endif |
| 261 | , zDirPart |
| 262 | #if USE_SEE |
| 263 | , zDirPart |
| 264 | #endif |
| 265 | ) ){ |
| 266 | blob_append_sql(&html, |
| 267 | "<s>%h</s> (directory/repo name collision)\n", |
| 268 | zName); |
| 269 | }else{ |
| 270 | blob_append_sql(&html, |
| @@ -300,11 +320,11 @@ | |
| 320 | }else{ |
| 321 | /* If no repositories were found that had the "repolist_skin" |
| 322 | ** property set, then use a default skin */ |
| 323 | @ <html> |
| 324 | @ <head> |
| 325 | @ <base href="%s(g.zBaseURL)/"> |
| 326 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 327 | @ <title>Repository List</title> |
| 328 | @ </head> |
| 329 | @ <body> |
| 330 | @ <h1 align="center">Fossil Repositories</h1> |
| 331 |
+16
-16
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -54,13 +54,13 @@ | ||
| 54 | 54 | login_needed(g.anon.RdTkt || g.anon.NewTkt); |
| 55 | 55 | return; |
| 56 | 56 | } |
| 57 | 57 | style_header("Ticket Main Menu"); |
| 58 | 58 | ticket_standard_submenu(T_ALL_BUT(T_REPLIST)); |
| 59 | - if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1); | |
| 59 | + if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br>\n", -1); | |
| 60 | 60 | zScript = ticket_reportlist_code(); |
| 61 | - if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1); | |
| 61 | + if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br>\n", -1); | |
| 62 | 62 | |
| 63 | 63 | blob_zero(&ril); |
| 64 | 64 | ticket_init(); |
| 65 | 65 | |
| 66 | 66 | db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title"); |
| @@ -105,11 +105,11 @@ | ||
| 105 | 105 | Th_Store("report_items", blob_str(&ril)); |
| 106 | 106 | |
| 107 | 107 | Th_Render(zScript); |
| 108 | 108 | |
| 109 | 109 | blob_reset(&ril); |
| 110 | - if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1); | |
| 110 | + if( g.thTrace ) Th_Trace("END_REPORTLIST<br>\n", -1); | |
| 111 | 111 | |
| 112 | 112 | style_finish_page(); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 115 | /* |
| @@ -593,38 +593,38 @@ | ||
| 593 | 593 | style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format"); |
| 594 | 594 | if( zErr ){ |
| 595 | 595 | @ <blockquote class="reportError">%h(zErr)</blockquote> |
| 596 | 596 | } |
| 597 | 597 | @ <form action="rptedit" method="post"><div> |
| 598 | - @ <input type="hidden" name="rn" value="%d(rn)" /> | |
| 599 | - @ <p>Report Title:<br /> | |
| 600 | - @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p> | |
| 601 | - @ <p>Enter a complete SQL query statement against the "TICKET" table:<br /> | |
| 598 | + @ <input type="hidden" name="rn" value="%d(rn)"> | |
| 599 | + @ <p>Report Title:<br> | |
| 600 | + @ <input type="text" name="t" value="%h(zTitle)" size="60"></p> | |
| 601 | + @ <p>Enter a complete SQL query statement against the "TICKET" table:<br> | |
| 602 | 602 | @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> |
| 603 | 603 | @ </p> |
| 604 | 604 | login_insert_csrf_secret(); |
| 605 | 605 | if( g.perm.Admin ){ |
| 606 | 606 | @ <p>Report owner: |
| 607 | - @ <input type="text" name="w" size="20" value="%h(zOwner)" /> | |
| 607 | + @ <input type="text" name="w" size="20" value="%h(zOwner)"> | |
| 608 | 608 | @ </p> |
| 609 | 609 | @ <p>Tag: |
| 610 | - @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")" /> | |
| 610 | + @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")"> | |
| 611 | 611 | @ </p> |
| 612 | 612 | } else { |
| 613 | - @ <input type="hidden" name="w" value="%h(zOwner)" /> | |
| 613 | + @ <input type="hidden" name="w" value="%h(zOwner)"> | |
| 614 | 614 | if( zTag && zTag[0] ){ |
| 615 | - @ <input type="hidden" name="x" value="%h(zTag)" /> | |
| 615 | + @ <input type="hidden" name="x" value="%h(zTag)"> | |
| 616 | 616 | } |
| 617 | 617 | } |
| 618 | 618 | @ <p>Enter an optional color key in the following box. (If blank, no |
| 619 | 619 | @ color key is displayed.) Each line contains the text for a single |
| 620 | 620 | @ entry in the key. The first token of each line is the background |
| 621 | - @ color for that line.<br /> | |
| 621 | + @ color for that line.<br> | |
| 622 | 622 | @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea> |
| 623 | 623 | @ </p> |
| 624 | 624 | |
| 625 | - @ <p>Optional human-readable description for this report<br /> | |
| 625 | + @ <p>Optional human-readable description for this report<br> | |
| 626 | 626 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 627 | 627 | mimetype_option_menu(zMimetype, "m"); |
| 628 | 628 | @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \ |
| 629 | 629 | @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea> |
| 630 | 630 | @ </p> |
| @@ -637,13 +637,13 @@ | ||
| 637 | 637 | @ </form> |
| 638 | 638 | report_format_hints(); |
| 639 | 639 | style_finish_page(); |
| 640 | 640 | return; |
| 641 | 641 | } |
| 642 | - @ <input type="submit" value="Apply Changes" /> | |
| 642 | + @ <input type="submit" value="Apply Changes"> | |
| 643 | 643 | if( rn>0 ){ |
| 644 | - @ <input type="submit" value="Delete This Report" name="del1" /> | |
| 644 | + @ <input type="submit" value="Delete This Report" name="del1"> | |
| 645 | 645 | } |
| 646 | 646 | @ </div></form> |
| 647 | 647 | report_format_hints(); |
| 648 | 648 | style_finish_page(); |
| 649 | 649 | } |
| @@ -657,11 +657,11 @@ | ||
| 657 | 657 | zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'"); |
| 658 | 658 | if( zSchema==0 ){ |
| 659 | 659 | zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema" |
| 660 | 660 | " WHERE name='ticket'"); |
| 661 | 661 | } |
| 662 | - @ <hr /><h3>TICKET Schema</h3> | |
| 662 | + @ <hr><h3>TICKET Schema</h3> | |
| 663 | 663 | @ <blockquote><pre> |
| 664 | 664 | @ <code class="language-sql">%h(zSchema)</code> |
| 665 | 665 | @ </pre></blockquote> |
| 666 | 666 | @ <h3>Notes</h3> |
| 667 | 667 | @ <ul> |
| 668 | 668 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -54,13 +54,13 @@ | |
| 54 | login_needed(g.anon.RdTkt || g.anon.NewTkt); |
| 55 | return; |
| 56 | } |
| 57 | style_header("Ticket Main Menu"); |
| 58 | ticket_standard_submenu(T_ALL_BUT(T_REPLIST)); |
| 59 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1); |
| 60 | zScript = ticket_reportlist_code(); |
| 61 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1); |
| 62 | |
| 63 | blob_zero(&ril); |
| 64 | ticket_init(); |
| 65 | |
| 66 | db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title"); |
| @@ -105,11 +105,11 @@ | |
| 105 | Th_Store("report_items", blob_str(&ril)); |
| 106 | |
| 107 | Th_Render(zScript); |
| 108 | |
| 109 | blob_reset(&ril); |
| 110 | if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1); |
| 111 | |
| 112 | style_finish_page(); |
| 113 | } |
| 114 | |
| 115 | /* |
| @@ -593,38 +593,38 @@ | |
| 593 | style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format"); |
| 594 | if( zErr ){ |
| 595 | @ <blockquote class="reportError">%h(zErr)</blockquote> |
| 596 | } |
| 597 | @ <form action="rptedit" method="post"><div> |
| 598 | @ <input type="hidden" name="rn" value="%d(rn)" /> |
| 599 | @ <p>Report Title:<br /> |
| 600 | @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p> |
| 601 | @ <p>Enter a complete SQL query statement against the "TICKET" table:<br /> |
| 602 | @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> |
| 603 | @ </p> |
| 604 | login_insert_csrf_secret(); |
| 605 | if( g.perm.Admin ){ |
| 606 | @ <p>Report owner: |
| 607 | @ <input type="text" name="w" size="20" value="%h(zOwner)" /> |
| 608 | @ </p> |
| 609 | @ <p>Tag: |
| 610 | @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")" /> |
| 611 | @ </p> |
| 612 | } else { |
| 613 | @ <input type="hidden" name="w" value="%h(zOwner)" /> |
| 614 | if( zTag && zTag[0] ){ |
| 615 | @ <input type="hidden" name="x" value="%h(zTag)" /> |
| 616 | } |
| 617 | } |
| 618 | @ <p>Enter an optional color key in the following box. (If blank, no |
| 619 | @ color key is displayed.) Each line contains the text for a single |
| 620 | @ entry in the key. The first token of each line is the background |
| 621 | @ color for that line.<br /> |
| 622 | @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea> |
| 623 | @ </p> |
| 624 | |
| 625 | @ <p>Optional human-readable description for this report<br /> |
| 626 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 627 | mimetype_option_menu(zMimetype, "m"); |
| 628 | @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \ |
| 629 | @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea> |
| 630 | @ </p> |
| @@ -637,13 +637,13 @@ | |
| 637 | @ </form> |
| 638 | report_format_hints(); |
| 639 | style_finish_page(); |
| 640 | return; |
| 641 | } |
| 642 | @ <input type="submit" value="Apply Changes" /> |
| 643 | if( rn>0 ){ |
| 644 | @ <input type="submit" value="Delete This Report" name="del1" /> |
| 645 | } |
| 646 | @ </div></form> |
| 647 | report_format_hints(); |
| 648 | style_finish_page(); |
| 649 | } |
| @@ -657,11 +657,11 @@ | |
| 657 | zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'"); |
| 658 | if( zSchema==0 ){ |
| 659 | zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema" |
| 660 | " WHERE name='ticket'"); |
| 661 | } |
| 662 | @ <hr /><h3>TICKET Schema</h3> |
| 663 | @ <blockquote><pre> |
| 664 | @ <code class="language-sql">%h(zSchema)</code> |
| 665 | @ </pre></blockquote> |
| 666 | @ <h3>Notes</h3> |
| 667 | @ <ul> |
| 668 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -54,13 +54,13 @@ | |
| 54 | login_needed(g.anon.RdTkt || g.anon.NewTkt); |
| 55 | return; |
| 56 | } |
| 57 | style_header("Ticket Main Menu"); |
| 58 | ticket_standard_submenu(T_ALL_BUT(T_REPLIST)); |
| 59 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br>\n", -1); |
| 60 | zScript = ticket_reportlist_code(); |
| 61 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br>\n", -1); |
| 62 | |
| 63 | blob_zero(&ril); |
| 64 | ticket_init(); |
| 65 | |
| 66 | db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title"); |
| @@ -105,11 +105,11 @@ | |
| 105 | Th_Store("report_items", blob_str(&ril)); |
| 106 | |
| 107 | Th_Render(zScript); |
| 108 | |
| 109 | blob_reset(&ril); |
| 110 | if( g.thTrace ) Th_Trace("END_REPORTLIST<br>\n", -1); |
| 111 | |
| 112 | style_finish_page(); |
| 113 | } |
| 114 | |
| 115 | /* |
| @@ -593,38 +593,38 @@ | |
| 593 | style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format"); |
| 594 | if( zErr ){ |
| 595 | @ <blockquote class="reportError">%h(zErr)</blockquote> |
| 596 | } |
| 597 | @ <form action="rptedit" method="post"><div> |
| 598 | @ <input type="hidden" name="rn" value="%d(rn)"> |
| 599 | @ <p>Report Title:<br> |
| 600 | @ <input type="text" name="t" value="%h(zTitle)" size="60"></p> |
| 601 | @ <p>Enter a complete SQL query statement against the "TICKET" table:<br> |
| 602 | @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> |
| 603 | @ </p> |
| 604 | login_insert_csrf_secret(); |
| 605 | if( g.perm.Admin ){ |
| 606 | @ <p>Report owner: |
| 607 | @ <input type="text" name="w" size="20" value="%h(zOwner)"> |
| 608 | @ </p> |
| 609 | @ <p>Tag: |
| 610 | @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")"> |
| 611 | @ </p> |
| 612 | } else { |
| 613 | @ <input type="hidden" name="w" value="%h(zOwner)"> |
| 614 | if( zTag && zTag[0] ){ |
| 615 | @ <input type="hidden" name="x" value="%h(zTag)"> |
| 616 | } |
| 617 | } |
| 618 | @ <p>Enter an optional color key in the following box. (If blank, no |
| 619 | @ color key is displayed.) Each line contains the text for a single |
| 620 | @ entry in the key. The first token of each line is the background |
| 621 | @ color for that line.<br> |
| 622 | @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea> |
| 623 | @ </p> |
| 624 | |
| 625 | @ <p>Optional human-readable description for this report<br> |
| 626 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 627 | mimetype_option_menu(zMimetype, "m"); |
| 628 | @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \ |
| 629 | @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea> |
| 630 | @ </p> |
| @@ -637,13 +637,13 @@ | |
| 637 | @ </form> |
| 638 | report_format_hints(); |
| 639 | style_finish_page(); |
| 640 | return; |
| 641 | } |
| 642 | @ <input type="submit" value="Apply Changes"> |
| 643 | if( rn>0 ){ |
| 644 | @ <input type="submit" value="Delete This Report" name="del1"> |
| 645 | } |
| 646 | @ </div></form> |
| 647 | report_format_hints(); |
| 648 | style_finish_page(); |
| 649 | } |
| @@ -657,11 +657,11 @@ | |
| 657 | zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'"); |
| 658 | if( zSchema==0 ){ |
| 659 | zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema" |
| 660 | " WHERE name='ticket'"); |
| 661 | } |
| 662 | @ <hr><h3>TICKET Schema</h3> |
| 663 | @ <blockquote><pre> |
| 664 | @ <code class="language-sql">%h(zSchema)</code> |
| 665 | @ </pre></blockquote> |
| 666 | @ <h3>Notes</h3> |
| 667 | @ <ul> |
| 668 |
+109
-15
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -926,11 +926,11 @@ | ||
| 926 | 926 | static const char *zSnippetCall; |
| 927 | 927 | if( srchFlags==0 ) return; |
| 928 | 928 | sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0, |
| 929 | 929 | search_rank_sqlfunc, 0, 0); |
| 930 | 930 | for(i=0; zPat[i]; i++){ |
| 931 | - if( zPat[i]=='-' || zPat[i]=='"' ) zPat[i] = ' '; | |
| 931 | + if( (zPat[i]&0x80)!=0 || !fossil_isalnum(zPat[i]) ) zPat[i] = ' '; | |
| 932 | 932 | } |
| 933 | 933 | blob_init(&sql, 0, 0); |
| 934 | 934 | if( search_index_type(0)==4 ){ |
| 935 | 935 | /* If this repo is still using the legacy FTS4 search index, then |
| 936 | 936 | ** the snippet() function is slightly different */ |
| @@ -1078,11 +1078,11 @@ | ||
| 1078 | 1078 | nRow++; |
| 1079 | 1079 | @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a> |
| 1080 | 1080 | if( fDebug ){ |
| 1081 | 1081 | @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4)) |
| 1082 | 1082 | } |
| 1083 | - @ <br /><span class='snippet'>%z(cleanSnippet(zSnippet)) \ | |
| 1083 | + @ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \ | |
| 1084 | 1084 | if( zDate && zDate[0] && strstr(zLabel,zDate)==0 ){ |
| 1085 | 1085 | @ <small>(%h(zDate))</small> |
| 1086 | 1086 | } |
| 1087 | 1087 | @ </span></li> |
| 1088 | 1088 | if( nLimit && nRow>=nLimit ) break; |
| @@ -1512,11 +1512,14 @@ | ||
| 1512 | 1512 | fossil_print("%s\n",blob_str(&out)); |
| 1513 | 1513 | blob_reset(&in); |
| 1514 | 1514 | blob_reset(&out); |
| 1515 | 1515 | } |
| 1516 | 1516 | |
| 1517 | -/* The schema for the full-text index | |
| 1517 | +/* | |
| 1518 | +** The schema for the full-text index. The %s part must be an empty | |
| 1519 | +** string or a comma followed by additional flags for the FTS virtual | |
| 1520 | +** table. | |
| 1518 | 1521 | */ |
| 1519 | 1522 | static const char zFtsSchema[] = |
| 1520 | 1523 | @ -- One entry for each possible search result |
| 1521 | 1524 | @ CREATE TABLE IF NOT EXISTS repository.ftsdocs( |
| 1522 | 1525 | @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid |
| @@ -1542,18 +1545,100 @@ | ||
| 1542 | 1545 | static const char zFtsDrop[] = |
| 1543 | 1546 | @ DROP TABLE IF EXISTS repository.ftsidx; |
| 1544 | 1547 | @ DROP VIEW IF EXISTS repository.ftscontent; |
| 1545 | 1548 | @ DROP TABLE IF EXISTS repository.ftsdocs; |
| 1546 | 1549 | ; |
| 1550 | + | |
| 1551 | +#if INTERFACE | |
| 1552 | +/* | |
| 1553 | +** Values for the search-tokenizer config option. | |
| 1554 | +*/ | |
| 1555 | +#define FTS5TOK_NONE 0 /* no FTS stemmer */ | |
| 1556 | +#define FTS5TOK_PORTER 1 /* porter stemmer */ | |
| 1557 | +#define FTS5TOK_TRIGRAM 3 /* trigram stemmer */ | |
| 1558 | +#endif | |
| 1559 | + | |
| 1560 | +/* | |
| 1561 | +** Cached FTS5TOK_xyz value for search_tokenizer_type() and | |
| 1562 | +** friends. | |
| 1563 | +*/ | |
| 1564 | +static int iFtsTokenizer = -1; | |
| 1565 | + | |
| 1566 | +/* | |
| 1567 | +** Returns one of the FTS5TOK_xyz values, depending on the value of | |
| 1568 | +** the search-tokenizer config entry, defaulting to FTS5TOK_NONE. The | |
| 1569 | +** result of the first call is cached for subsequent calls unless | |
| 1570 | +** bRecheck is true. | |
| 1571 | +*/ | |
| 1572 | +int search_tokenizer_type(int bRecheck){ | |
| 1573 | + char *z; | |
| 1574 | + if( iFtsTokenizer>=0 && bRecheck==0 ){ | |
| 1575 | + return iFtsTokenizer; | |
| 1576 | + } | |
| 1577 | + z = db_get("search-tokenizer",0); | |
| 1578 | + if( 0==z ){ | |
| 1579 | + iFtsTokenizer = FTS5TOK_NONE; | |
| 1580 | + }else if(0==fossil_strcmp(z,"porter")){ | |
| 1581 | + iFtsTokenizer = FTS5TOK_PORTER; | |
| 1582 | + }else if(0==fossil_strcmp(z,"trigram")){ | |
| 1583 | + iFtsTokenizer = FTS5TOK_TRIGRAM; | |
| 1584 | + }else{ | |
| 1585 | + iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE; | |
| 1586 | + } | |
| 1587 | + fossil_free(z); | |
| 1588 | + return iFtsTokenizer; | |
| 1589 | +} | |
| 1590 | + | |
| 1591 | +/* | |
| 1592 | +** Returns a string value suitable for use as the search-tokenizer | |
| 1593 | +** setting's value, depending on the value of z. If z is 0 then the | |
| 1594 | +** current search-tokenizer value is used as the basis for formulating | |
| 1595 | +** the result (which may differ from the current value but will have | |
| 1596 | +** the same meaning). Any unknown/unsupported value is interpreted as | |
| 1597 | +** "off". | |
| 1598 | +*/ | |
| 1599 | +const char *search_tokenizer_for_string(const char *z){ | |
| 1600 | + char * zTmp = 0; | |
| 1601 | + const char *zRc = 0; | |
| 1602 | + | |
| 1603 | + if( 0==z ){ | |
| 1604 | + z = zTmp = db_get("search-tokenizer",0); | |
| 1605 | + } | |
| 1606 | + if( 0==z ){ | |
| 1607 | + zRc = "off"; | |
| 1608 | + }else if( 0==fossil_strcmp(z,"porter") ){ | |
| 1609 | + zRc = "porter"; | |
| 1610 | + }else if( 0==fossil_strcmp(z,"trigram") ){ | |
| 1611 | + zRc = "trigram"; | |
| 1612 | + }else{ | |
| 1613 | + zRc = is_truth(z) ? "porter" : "off"; | |
| 1614 | + } | |
| 1615 | + fossil_free(zTmp); | |
| 1616 | + return zRc; | |
| 1617 | +} | |
| 1618 | + | |
| 1619 | +/* | |
| 1620 | +** Sets the search-tokenizer config setting to the value of | |
| 1621 | +** search_tokenizer_for_string(zName). | |
| 1622 | +*/ | |
| 1623 | +void search_set_tokenizer(const char *zName){ | |
| 1624 | + db_set("search-tokenizer", search_tokenizer_for_string( zName ), 0); | |
| 1625 | + iFtsTokenizer = -1; | |
| 1626 | +} | |
| 1547 | 1627 | |
| 1548 | 1628 | /* |
| 1549 | 1629 | ** Create or drop the tables associated with a full-text index. |
| 1550 | 1630 | */ |
| 1551 | 1631 | static int searchIdxExists = -1; |
| 1552 | 1632 | void search_create_index(void){ |
| 1553 | - int useStemmer = db_get_boolean("search-stemmer",0); | |
| 1554 | - const char *zExtra = useStemmer ? ",tokenize=porter" : ""; | |
| 1633 | + const int useTokenizer = search_tokenizer_type(0); | |
| 1634 | + const char *zExtra; | |
| 1635 | + switch(useTokenizer){ | |
| 1636 | + case FTS5TOK_PORTER: zExtra = ",tokenize=porter"; break; | |
| 1637 | + case FTS5TOK_TRIGRAM: zExtra = ",tokenize=trigram"; break; | |
| 1638 | + default: zExtra = ""; break; | |
| 1639 | + } | |
| 1555 | 1640 | search_sql_setup(g.db); |
| 1556 | 1641 | db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/); |
| 1557 | 1642 | searchIdxExists = 1; |
| 1558 | 1643 | } |
| 1559 | 1644 | void search_drop_index(void){ |
| @@ -1894,12 +1979,14 @@ | ||
| 1894 | 1979 | ** enable cdtwe Enable various kinds of search. c=Check-ins, |
| 1895 | 1980 | ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. |
| 1896 | 1981 | ** |
| 1897 | 1982 | ** disable cdtwe Disable various kinds of search |
| 1898 | 1983 | ** |
| 1899 | -** stemmer (on|off) Turn the Porter stemmer on or off for indexed | |
| 1900 | -** search. (Unindexed search is never stemmed.) | |
| 1984 | +** tokenizer VALUE Select a tokenizer for indexed search. VALUE | |
| 1985 | +** may be one of (porter, on, off, trigram), and | |
| 1986 | +** "on" is equivalent to "porter". Unindexed | |
| 1987 | +** search never uses tokenization or stemming. | |
| 1901 | 1988 | ** |
| 1902 | 1989 | ** The current search settings are displayed after any changes are applied. |
| 1903 | 1990 | ** Run this command with no arguments to simply see the settings. |
| 1904 | 1991 | */ |
| 1905 | 1992 | void fts_config_cmd(void){ |
| @@ -1909,11 +1996,11 @@ | ||
| 1909 | 1996 | } aCmd[] = { |
| 1910 | 1997 | { 1, "reindex" }, |
| 1911 | 1998 | { 2, "index" }, |
| 1912 | 1999 | { 3, "disable" }, |
| 1913 | 2000 | { 4, "enable" }, |
| 1914 | - { 5, "stemmer" }, | |
| 2001 | + { 5, "tokenizer"}, | |
| 1915 | 2002 | }; |
| 1916 | 2003 | static const struct { |
| 1917 | 2004 | const char *zSetting; |
| 1918 | 2005 | const char *zName; |
| 1919 | 2006 | const char *zSw; |
| @@ -1966,16 +2053,23 @@ | ||
| 1966 | 2053 | for(j=0; j<count(aSetng); j++){ |
| 1967 | 2054 | if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ |
| 1968 | 2055 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 1969 | 2056 | } |
| 1970 | 2057 | } |
| 2058 | + }else if( iCmd==5 ){ | |
| 2059 | + int iOldTokenizer, iNewTokenizer; | |
| 2060 | + if( g.argc<4 ) usage("tokenizer porter|on|off|trigram"); | |
| 2061 | + iOldTokenizer = search_tokenizer_type(0); | |
| 2062 | + db_set("search-tokenizer", | |
| 2063 | + search_tokenizer_for_string(g.argv[3]), 0); | |
| 2064 | + iNewTokenizer = search_tokenizer_type(1); | |
| 2065 | + if( iOldTokenizer!=iNewTokenizer ){ | |
| 2066 | + /* Drop or rebuild index if tokenizer changes. */ | |
| 2067 | + iAction = 1 + ((iOldTokenizer && iNewTokenizer) | |
| 2068 | + ? 1 : (iNewTokenizer ? 1 : 0)); | |
| 2069 | + } | |
| 1971 | 2070 | } |
| 1972 | - if( iCmd==5 ){ | |
| 1973 | - if( g.argc<4 ) usage("porter ON/OFF"); | |
| 1974 | - db_set_int("search-stemmer", is_truth(g.argv[3]), 0); | |
| 1975 | - } | |
| 1976 | - | |
| 1977 | 2071 | |
| 1978 | 2072 | /* destroy or rebuild the index, if requested */ |
| 1979 | 2073 | if( iAction>=1 ){ |
| 1980 | 2074 | search_drop_index(); |
| 1981 | 2075 | } |
| @@ -1986,12 +2080,12 @@ | ||
| 1986 | 2080 | /* Always show the status before ending */ |
| 1987 | 2081 | for(i=0; i<count(aSetng); i++){ |
| 1988 | 2082 | fossil_print("%-17s %s\n", aSetng[i].zName, |
| 1989 | 2083 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 1990 | 2084 | } |
| 1991 | - fossil_print("%-17s %s\n", "Porter stemmer:", | |
| 1992 | - db_get_boolean("search-stemmer",0) ? "on" : "off"); | |
| 2085 | + fossil_print("%-17s %s\n", "tokenizer:", | |
| 2086 | + search_tokenizer_for_string(0)); | |
| 1993 | 2087 | if( search_index_exists() ){ |
| 1994 | 2088 | fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); |
| 1995 | 2089 | fossil_print("%-17s %d\n", "documents:", |
| 1996 | 2090 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 1997 | 2091 | }else{ |
| 1998 | 2092 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -926,11 +926,11 @@ | |
| 926 | static const char *zSnippetCall; |
| 927 | if( srchFlags==0 ) return; |
| 928 | sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0, |
| 929 | search_rank_sqlfunc, 0, 0); |
| 930 | for(i=0; zPat[i]; i++){ |
| 931 | if( zPat[i]=='-' || zPat[i]=='"' ) zPat[i] = ' '; |
| 932 | } |
| 933 | blob_init(&sql, 0, 0); |
| 934 | if( search_index_type(0)==4 ){ |
| 935 | /* If this repo is still using the legacy FTS4 search index, then |
| 936 | ** the snippet() function is slightly different */ |
| @@ -1078,11 +1078,11 @@ | |
| 1078 | nRow++; |
| 1079 | @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a> |
| 1080 | if( fDebug ){ |
| 1081 | @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4)) |
| 1082 | } |
| 1083 | @ <br /><span class='snippet'>%z(cleanSnippet(zSnippet)) \ |
| 1084 | if( zDate && zDate[0] && strstr(zLabel,zDate)==0 ){ |
| 1085 | @ <small>(%h(zDate))</small> |
| 1086 | } |
| 1087 | @ </span></li> |
| 1088 | if( nLimit && nRow>=nLimit ) break; |
| @@ -1512,11 +1512,14 @@ | |
| 1512 | fossil_print("%s\n",blob_str(&out)); |
| 1513 | blob_reset(&in); |
| 1514 | blob_reset(&out); |
| 1515 | } |
| 1516 | |
| 1517 | /* The schema for the full-text index |
| 1518 | */ |
| 1519 | static const char zFtsSchema[] = |
| 1520 | @ -- One entry for each possible search result |
| 1521 | @ CREATE TABLE IF NOT EXISTS repository.ftsdocs( |
| 1522 | @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid |
| @@ -1542,18 +1545,100 @@ | |
| 1542 | static const char zFtsDrop[] = |
| 1543 | @ DROP TABLE IF EXISTS repository.ftsidx; |
| 1544 | @ DROP VIEW IF EXISTS repository.ftscontent; |
| 1545 | @ DROP TABLE IF EXISTS repository.ftsdocs; |
| 1546 | ; |
| 1547 | |
| 1548 | /* |
| 1549 | ** Create or drop the tables associated with a full-text index. |
| 1550 | */ |
| 1551 | static int searchIdxExists = -1; |
| 1552 | void search_create_index(void){ |
| 1553 | int useStemmer = db_get_boolean("search-stemmer",0); |
| 1554 | const char *zExtra = useStemmer ? ",tokenize=porter" : ""; |
| 1555 | search_sql_setup(g.db); |
| 1556 | db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/); |
| 1557 | searchIdxExists = 1; |
| 1558 | } |
| 1559 | void search_drop_index(void){ |
| @@ -1894,12 +1979,14 @@ | |
| 1894 | ** enable cdtwe Enable various kinds of search. c=Check-ins, |
| 1895 | ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. |
| 1896 | ** |
| 1897 | ** disable cdtwe Disable various kinds of search |
| 1898 | ** |
| 1899 | ** stemmer (on|off) Turn the Porter stemmer on or off for indexed |
| 1900 | ** search. (Unindexed search is never stemmed.) |
| 1901 | ** |
| 1902 | ** The current search settings are displayed after any changes are applied. |
| 1903 | ** Run this command with no arguments to simply see the settings. |
| 1904 | */ |
| 1905 | void fts_config_cmd(void){ |
| @@ -1909,11 +1996,11 @@ | |
| 1909 | } aCmd[] = { |
| 1910 | { 1, "reindex" }, |
| 1911 | { 2, "index" }, |
| 1912 | { 3, "disable" }, |
| 1913 | { 4, "enable" }, |
| 1914 | { 5, "stemmer" }, |
| 1915 | }; |
| 1916 | static const struct { |
| 1917 | const char *zSetting; |
| 1918 | const char *zName; |
| 1919 | const char *zSw; |
| @@ -1966,16 +2053,23 @@ | |
| 1966 | for(j=0; j<count(aSetng); j++){ |
| 1967 | if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ |
| 1968 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 1969 | } |
| 1970 | } |
| 1971 | } |
| 1972 | if( iCmd==5 ){ |
| 1973 | if( g.argc<4 ) usage("porter ON/OFF"); |
| 1974 | db_set_int("search-stemmer", is_truth(g.argv[3]), 0); |
| 1975 | } |
| 1976 | |
| 1977 | |
| 1978 | /* destroy or rebuild the index, if requested */ |
| 1979 | if( iAction>=1 ){ |
| 1980 | search_drop_index(); |
| 1981 | } |
| @@ -1986,12 +2080,12 @@ | |
| 1986 | /* Always show the status before ending */ |
| 1987 | for(i=0; i<count(aSetng); i++){ |
| 1988 | fossil_print("%-17s %s\n", aSetng[i].zName, |
| 1989 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 1990 | } |
| 1991 | fossil_print("%-17s %s\n", "Porter stemmer:", |
| 1992 | db_get_boolean("search-stemmer",0) ? "on" : "off"); |
| 1993 | if( search_index_exists() ){ |
| 1994 | fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); |
| 1995 | fossil_print("%-17s %d\n", "documents:", |
| 1996 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 1997 | }else{ |
| 1998 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -926,11 +926,11 @@ | |
| 926 | static const char *zSnippetCall; |
| 927 | if( srchFlags==0 ) return; |
| 928 | sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0, |
| 929 | search_rank_sqlfunc, 0, 0); |
| 930 | for(i=0; zPat[i]; i++){ |
| 931 | if( (zPat[i]&0x80)!=0 || !fossil_isalnum(zPat[i]) ) zPat[i] = ' '; |
| 932 | } |
| 933 | blob_init(&sql, 0, 0); |
| 934 | if( search_index_type(0)==4 ){ |
| 935 | /* If this repo is still using the legacy FTS4 search index, then |
| 936 | ** the snippet() function is slightly different */ |
| @@ -1078,11 +1078,11 @@ | |
| 1078 | nRow++; |
| 1079 | @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a> |
| 1080 | if( fDebug ){ |
| 1081 | @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4)) |
| 1082 | } |
| 1083 | @ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \ |
| 1084 | if( zDate && zDate[0] && strstr(zLabel,zDate)==0 ){ |
| 1085 | @ <small>(%h(zDate))</small> |
| 1086 | } |
| 1087 | @ </span></li> |
| 1088 | if( nLimit && nRow>=nLimit ) break; |
| @@ -1512,11 +1512,14 @@ | |
| 1512 | fossil_print("%s\n",blob_str(&out)); |
| 1513 | blob_reset(&in); |
| 1514 | blob_reset(&out); |
| 1515 | } |
| 1516 | |
| 1517 | /* |
| 1518 | ** The schema for the full-text index. The %s part must be an empty |
| 1519 | ** string or a comma followed by additional flags for the FTS virtual |
| 1520 | ** table. |
| 1521 | */ |
| 1522 | static const char zFtsSchema[] = |
| 1523 | @ -- One entry for each possible search result |
| 1524 | @ CREATE TABLE IF NOT EXISTS repository.ftsdocs( |
| 1525 | @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid |
| @@ -1542,18 +1545,100 @@ | |
| 1545 | static const char zFtsDrop[] = |
| 1546 | @ DROP TABLE IF EXISTS repository.ftsidx; |
| 1547 | @ DROP VIEW IF EXISTS repository.ftscontent; |
| 1548 | @ DROP TABLE IF EXISTS repository.ftsdocs; |
| 1549 | ; |
| 1550 | |
| 1551 | #if INTERFACE |
| 1552 | /* |
| 1553 | ** Values for the search-tokenizer config option. |
| 1554 | */ |
| 1555 | #define FTS5TOK_NONE 0 /* no FTS stemmer */ |
| 1556 | #define FTS5TOK_PORTER 1 /* porter stemmer */ |
| 1557 | #define FTS5TOK_TRIGRAM 3 /* trigram stemmer */ |
| 1558 | #endif |
| 1559 | |
| 1560 | /* |
| 1561 | ** Cached FTS5TOK_xyz value for search_tokenizer_type() and |
| 1562 | ** friends. |
| 1563 | */ |
| 1564 | static int iFtsTokenizer = -1; |
| 1565 | |
| 1566 | /* |
| 1567 | ** Returns one of the FTS5TOK_xyz values, depending on the value of |
| 1568 | ** the search-tokenizer config entry, defaulting to FTS5TOK_NONE. The |
| 1569 | ** result of the first call is cached for subsequent calls unless |
| 1570 | ** bRecheck is true. |
| 1571 | */ |
| 1572 | int search_tokenizer_type(int bRecheck){ |
| 1573 | char *z; |
| 1574 | if( iFtsTokenizer>=0 && bRecheck==0 ){ |
| 1575 | return iFtsTokenizer; |
| 1576 | } |
| 1577 | z = db_get("search-tokenizer",0); |
| 1578 | if( 0==z ){ |
| 1579 | iFtsTokenizer = FTS5TOK_NONE; |
| 1580 | }else if(0==fossil_strcmp(z,"porter")){ |
| 1581 | iFtsTokenizer = FTS5TOK_PORTER; |
| 1582 | }else if(0==fossil_strcmp(z,"trigram")){ |
| 1583 | iFtsTokenizer = FTS5TOK_TRIGRAM; |
| 1584 | }else{ |
| 1585 | iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE; |
| 1586 | } |
| 1587 | fossil_free(z); |
| 1588 | return iFtsTokenizer; |
| 1589 | } |
| 1590 | |
| 1591 | /* |
| 1592 | ** Returns a string value suitable for use as the search-tokenizer |
| 1593 | ** setting's value, depending on the value of z. If z is 0 then the |
| 1594 | ** current search-tokenizer value is used as the basis for formulating |
| 1595 | ** the result (which may differ from the current value but will have |
| 1596 | ** the same meaning). Any unknown/unsupported value is interpreted as |
| 1597 | ** "off". |
| 1598 | */ |
| 1599 | const char *search_tokenizer_for_string(const char *z){ |
| 1600 | char * zTmp = 0; |
| 1601 | const char *zRc = 0; |
| 1602 | |
| 1603 | if( 0==z ){ |
| 1604 | z = zTmp = db_get("search-tokenizer",0); |
| 1605 | } |
| 1606 | if( 0==z ){ |
| 1607 | zRc = "off"; |
| 1608 | }else if( 0==fossil_strcmp(z,"porter") ){ |
| 1609 | zRc = "porter"; |
| 1610 | }else if( 0==fossil_strcmp(z,"trigram") ){ |
| 1611 | zRc = "trigram"; |
| 1612 | }else{ |
| 1613 | zRc = is_truth(z) ? "porter" : "off"; |
| 1614 | } |
| 1615 | fossil_free(zTmp); |
| 1616 | return zRc; |
| 1617 | } |
| 1618 | |
| 1619 | /* |
| 1620 | ** Sets the search-tokenizer config setting to the value of |
| 1621 | ** search_tokenizer_for_string(zName). |
| 1622 | */ |
| 1623 | void search_set_tokenizer(const char *zName){ |
| 1624 | db_set("search-tokenizer", search_tokenizer_for_string( zName ), 0); |
| 1625 | iFtsTokenizer = -1; |
| 1626 | } |
| 1627 | |
| 1628 | /* |
| 1629 | ** Create or drop the tables associated with a full-text index. |
| 1630 | */ |
| 1631 | static int searchIdxExists = -1; |
| 1632 | void search_create_index(void){ |
| 1633 | const int useTokenizer = search_tokenizer_type(0); |
| 1634 | const char *zExtra; |
| 1635 | switch(useTokenizer){ |
| 1636 | case FTS5TOK_PORTER: zExtra = ",tokenize=porter"; break; |
| 1637 | case FTS5TOK_TRIGRAM: zExtra = ",tokenize=trigram"; break; |
| 1638 | default: zExtra = ""; break; |
| 1639 | } |
| 1640 | search_sql_setup(g.db); |
| 1641 | db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/); |
| 1642 | searchIdxExists = 1; |
| 1643 | } |
| 1644 | void search_drop_index(void){ |
| @@ -1894,12 +1979,14 @@ | |
| 1979 | ** enable cdtwe Enable various kinds of search. c=Check-ins, |
| 1980 | ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. |
| 1981 | ** |
| 1982 | ** disable cdtwe Disable various kinds of search |
| 1983 | ** |
| 1984 | ** tokenizer VALUE Select a tokenizer for indexed search. VALUE |
| 1985 | ** may be one of (porter, on, off, trigram), and |
| 1986 | ** "on" is equivalent to "porter". Unindexed |
| 1987 | ** search never uses tokenization or stemming. |
| 1988 | ** |
| 1989 | ** The current search settings are displayed after any changes are applied. |
| 1990 | ** Run this command with no arguments to simply see the settings. |
| 1991 | */ |
| 1992 | void fts_config_cmd(void){ |
| @@ -1909,11 +1996,11 @@ | |
| 1996 | } aCmd[] = { |
| 1997 | { 1, "reindex" }, |
| 1998 | { 2, "index" }, |
| 1999 | { 3, "disable" }, |
| 2000 | { 4, "enable" }, |
| 2001 | { 5, "tokenizer"}, |
| 2002 | }; |
| 2003 | static const struct { |
| 2004 | const char *zSetting; |
| 2005 | const char *zName; |
| 2006 | const char *zSw; |
| @@ -1966,16 +2053,23 @@ | |
| 2053 | for(j=0; j<count(aSetng); j++){ |
| 2054 | if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ |
| 2055 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 2056 | } |
| 2057 | } |
| 2058 | }else if( iCmd==5 ){ |
| 2059 | int iOldTokenizer, iNewTokenizer; |
| 2060 | if( g.argc<4 ) usage("tokenizer porter|on|off|trigram"); |
| 2061 | iOldTokenizer = search_tokenizer_type(0); |
| 2062 | db_set("search-tokenizer", |
| 2063 | search_tokenizer_for_string(g.argv[3]), 0); |
| 2064 | iNewTokenizer = search_tokenizer_type(1); |
| 2065 | if( iOldTokenizer!=iNewTokenizer ){ |
| 2066 | /* Drop or rebuild index if tokenizer changes. */ |
| 2067 | iAction = 1 + ((iOldTokenizer && iNewTokenizer) |
| 2068 | ? 1 : (iNewTokenizer ? 1 : 0)); |
| 2069 | } |
| 2070 | } |
| 2071 | |
| 2072 | /* destroy or rebuild the index, if requested */ |
| 2073 | if( iAction>=1 ){ |
| 2074 | search_drop_index(); |
| 2075 | } |
| @@ -1986,12 +2080,12 @@ | |
| 2080 | /* Always show the status before ending */ |
| 2081 | for(i=0; i<count(aSetng); i++){ |
| 2082 | fossil_print("%-17s %s\n", aSetng[i].zName, |
| 2083 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 2084 | } |
| 2085 | fossil_print("%-17s %s\n", "tokenizer:", |
| 2086 | search_tokenizer_for_string(0)); |
| 2087 | if( search_index_exists() ){ |
| 2088 | fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); |
| 2089 | fossil_print("%-17s %d\n", "documents:", |
| 2090 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 2091 | }else{ |
| 2092 |
+141
-124
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -219,11 +219,11 @@ | ||
| 219 | 219 | @ checked="checked" \ |
| 220 | 220 | } |
| 221 | 221 | if( disabled ){ |
| 222 | 222 | @ disabled="disabled" \ |
| 223 | 223 | } |
| 224 | - @ /> <b>%s(zLabel)</b></label> | |
| 224 | + @ > <b>%s(zLabel)</b></label> | |
| 225 | 225 | } |
| 226 | 226 | |
| 227 | 227 | /* |
| 228 | 228 | ** Generate an entry box for an attribute. |
| 229 | 229 | */ |
| @@ -251,11 +251,11 @@ | ||
| 251 | 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | 253 | if( disabled ){ |
| 254 | 254 | @ disabled="disabled" \ |
| 255 | 255 | } |
| 256 | - @ /> <b>%s(zLabel)</b> | |
| 256 | + @ > <b>%s(zLabel)</b> | |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | 259 | /* |
| 260 | 260 | ** Generate a text box for an attribute. |
| 261 | 261 | */ |
| @@ -371,11 +371,11 @@ | ||
| 371 | 371 | @ |
| 372 | 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | 373 | @ <blockquote> |
| 374 | 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | - @ <br /> | |
| 376 | + @ <br> | |
| 377 | 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | 379 | @ </blockquote> |
| 380 | 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | ||
| 412 | 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | 413 | @ defend the site against robots. |
| 414 | 414 | @ |
| 415 | 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | 416 | login_insert_csrf_secret(); |
| 417 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 418 | - @ <hr /> | |
| 417 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 418 | + @ <hr> | |
| 419 | 419 | addAutoHyperlinkSettings(); |
| 420 | - @ <hr /> | |
| 421 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 420 | + @ <hr> | |
| 421 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 422 | 422 | @ </div></form> |
| 423 | 423 | db_end_transaction(0); |
| 424 | 424 | style_finish_page(); |
| 425 | 425 | } |
| 426 | 426 | |
| @@ -444,12 +444,12 @@ | ||
| 444 | 444 | style_set_current_feature("setup"); |
| 445 | 445 | style_header("Access Control Settings"); |
| 446 | 446 | db_begin_transaction(); |
| 447 | 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | 448 | login_insert_csrf_secret(); |
| 449 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 450 | - @ <hr /> | |
| 449 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 450 | + @ <hr> | |
| 451 | 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | ||
| 457 | 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | 461 | @ "2" otherwise.) |
| 462 | - @ <hr /> | |
| 462 | + @ <hr> | |
| 463 | 463 | onoff_attribute("Require password for local access", |
| 464 | 464 | "localauth", "localauth", 0, 0); |
| 465 | 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | ||
| 483 | 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | 484 | @ in the CGI script. |
| 485 | 485 | @ </ol> |
| 486 | 486 | @ (Property: "localauth") |
| 487 | 487 | @ |
| 488 | - @ <hr /> | |
| 488 | + @ <hr> | |
| 489 | 489 | onoff_attribute("Enable /test_env", |
| 490 | 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | 493 | @ the /test_env page. |
| 494 | 494 | @ (Property: "test_env_enable") |
| 495 | 495 | @ </p> |
| 496 | 496 | @ |
| 497 | - @ <hr /> | |
| 497 | + @ <hr> | |
| 498 | 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | 502 | @ access the /artifact_stats page. |
| 503 | 503 | @ (Property: "artifact_stats_enable") |
| 504 | 504 | @ </p> |
| 505 | 505 | @ |
| 506 | - @ <hr /> | |
| 506 | + @ <hr> | |
| 507 | 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | 512 | @ (Property: "remote_user_ok") |
| 513 | 513 | @ </p> |
| 514 | 514 | @ |
| 515 | - @ <hr /> | |
| 515 | + @ <hr> | |
| 516 | 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | 521 | @ (Property: "http_authentication_ok") |
| 522 | 522 | @ </p> |
| 523 | 523 | @ |
| 524 | - @ <hr /> | |
| 524 | + @ <hr> | |
| 525 | 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | 526 | "8766", 0); |
| 527 | 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | 529 | @ to a year. |
| 530 | 530 | @ (Property: "cookie-expire")</p> |
| 531 | 531 | |
| 532 | - @ <hr /> | |
| 532 | + @ <hr> | |
| 533 | 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | 534 | "5000000", 0); |
| 535 | 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | 540 | |
| 541 | - @ <hr /> | |
| 541 | + @ <hr> | |
| 542 | 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | 543 | "30", 0); |
| 544 | 544 | |
| 545 | 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | ||
| 547 | 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | 549 | @ (Property: "max-download-time")</p> |
| 550 | 550 | |
| 551 | 551 | @ <a id="slal"></a> |
| 552 | - @ <hr /> | |
| 552 | + @ <hr> | |
| 553 | 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | 554 | "0.0", 0); |
| 555 | 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | ||
| 562 | 562 | @ (Property: "max-loadavg")</p> |
| 563 | 563 | |
| 564 | 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | 565 | ** are also accessible from the /setup_robot page. |
| 566 | 566 | */ |
| 567 | - @ <hr /> | |
| 567 | + @ <hr> | |
| 568 | 568 | addAutoHyperlinkSettings(); |
| 569 | 569 | |
| 570 | - @ <hr /> | |
| 570 | + @ <hr> | |
| 571 | 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | 576 | |
| 577 | - @ <hr /> | |
| 577 | + @ <hr> | |
| 578 | 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | 579 | "pubpage", "", 0); |
| 580 | 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | 581 | @ without needing a login and using the privileges given by the |
| 582 | 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | ||
| 587 | 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | 588 | @ allowing them to see the rest of the source code. |
| 589 | 589 | @ (Property: "public-pages") |
| 590 | 590 | @ </p> |
| 591 | 591 | |
| 592 | - @ <hr /> | |
| 592 | + @ <hr> | |
| 593 | 593 | onoff_attribute("Allow users to register themselves", |
| 594 | 594 | "self-register", "selfreg", 0, 0); |
| 595 | 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | 598 | @ enabled. |
| 599 | 599 | @ (Property: "self-register")</p> |
| 600 | 600 | |
| 601 | - @ <hr /> | |
| 601 | + @ <hr> | |
| 602 | 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | 605 | @ password reset page be sent to their email address of record. This |
| 606 | 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | 607 | @ administrator intervention. |
| 608 | 608 | @ (Property: "self-pw-reset")</p> |
| 609 | 609 | |
| 610 | - @ <hr /> | |
| 610 | + @ <hr> | |
| 611 | 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | 615 | @ added until the email address associated with the self-registration |
| 616 | 616 | @ has been verified. This setting only makes sense if |
| 617 | 617 | @ email notifications are enabled. |
| 618 | 618 | @ (Property: "selfreg-verify")</p> |
| 619 | 619 | |
| 620 | - @ <hr /> | |
| 620 | + @ <hr> | |
| 621 | 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | 625 | @ page, they are redirected to /register or /login. |
| 626 | 626 | @ (Property: "anon-subscribe")</p> |
| 627 | 627 | |
| 628 | - @ <hr /> | |
| 628 | + @ <hr> | |
| 629 | 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | 635 | @ organization or group based on their email address. |
| 636 | 636 | @ (Property: "auth-sub-email")</p> |
| 637 | 637 | |
| 638 | - @ <hr /> | |
| 638 | + @ <hr> | |
| 639 | 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | 640 | "defaultperms", "u", 0); |
| 641 | 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | ||
| 645 | 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | 647 | @ (Property: "default-perms") |
| 648 | 648 | @ </p> |
| 649 | 649 | |
| 650 | - @ <hr /> | |
| 650 | + @ <hr> | |
| 651 | 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | 658 | |
| 659 | - @ <hr /> | |
| 660 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 659 | + @ <hr> | |
| 660 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 661 | 661 | @ </div></form> |
| 662 | 662 | db_end_transaction(0); |
| 663 | 663 | style_finish_page(); |
| 664 | 664 | } |
| 665 | 665 | |
| @@ -762,11 +762,11 @@ | ||
| 762 | 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | 763 | login_insert_csrf_secret(); |
| 764 | 764 | @ To leave this login group press |
| 765 | 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | 766 | @ </form></p> |
| 767 | - @ <hr /><h2>Implementation Details</h2> | |
| 767 | + @ <hr><h2>Implementation Details</h2> | |
| 768 | 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | 771 | @ data-init-sort='1'> |
| 772 | 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | ||
| 814 | 814 | style_set_current_feature("setup"); |
| 815 | 815 | style_header("Timeline Display Preferences"); |
| 816 | 816 | db_begin_transaction(); |
| 817 | 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | 818 | login_insert_csrf_secret(); |
| 819 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 819 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 820 | 820 | |
| 821 | - @ <hr /> | |
| 821 | + @ <hr> | |
| 822 | 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | 827 | |
| 828 | - @ <hr /> | |
| 828 | + @ <hr> | |
| 829 | 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | 835 | |
| 836 | - @ <hr /> | |
| 836 | + @ <hr> | |
| 837 | 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | 842 | |
| 843 | - @ <hr /> | |
| 843 | + @ <hr> | |
| 844 | 844 | onoff_attribute("Break comments at newline characters", |
| 845 | 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | 847 | @ a line break on the display. |
| 848 | 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | 849 | |
| 850 | - @ <hr /> | |
| 850 | + @ <hr> | |
| 851 | 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | 852 | "timeline-utc", "utc", 1, 0); |
| 853 | 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | ||
| 866 | 866 | }else{ |
| 867 | 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | 868 | } |
| 869 | 869 | @ <p>(Property: "timeline-utc") |
| 870 | 870 | |
| 871 | - @ <hr /> | |
| 871 | + @ <hr> | |
| 872 | 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | 876 | |
| 877 | - @ <hr /> | |
| 877 | + @ <hr> | |
| 878 | 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | 879 | "tldl", "50", 0); |
| 880 | 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | 883 | |
| 884 | - @ <hr /> | |
| 884 | + @ <hr> | |
| 885 | 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | 892 | |
| 893 | - @ <hr /> | |
| 893 | + @ <hr> | |
| 894 | 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | 897 | @ "0" there is no length limit. |
| 898 | 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | 899 | |
| 900 | - @ <hr /> | |
| 900 | + @ <hr> | |
| 901 | 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | 903 | @ <br> |
| 904 | 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | ||
| 911 | 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | 915 | |
| 916 | - @ <hr /> | |
| 916 | + @ <hr> | |
| 917 | 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | ||
| 927 | 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | 929 | @ right of the entry. |
| 930 | 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | 931 | |
| 932 | - @ <hr /> | |
| 933 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 932 | + @ <hr> | |
| 933 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 934 | 934 | @ </div></form> |
| 935 | 935 | db_end_transaction(0); |
| 936 | 936 | style_finish_page(); |
| 937 | 937 | } |
| 938 | 938 | |
| @@ -964,11 +964,11 @@ | ||
| 964 | 964 | db_begin_transaction(); |
| 965 | 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | 966 | @ by the contents of managed files named |
| 967 | 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | - @ changed on this screen.</p><hr /><p> | |
| 969 | + @ changed on this screen.</p><hr><p> | |
| 970 | 970 | @ |
| 971 | 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | 973 | login_insert_csrf_secret(); |
| 974 | 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | ||
| 978 | 978 | onoff_attribute("", pSet->name, |
| 979 | 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | 982 | if( pSet->versionable ){ |
| 983 | - @ (v)<br /> | |
| 983 | + @ (v)<br> | |
| 984 | 984 | } else { |
| 985 | - @ <br /> | |
| 985 | + @ <br> | |
| 986 | 986 | } |
| 987 | 987 | } |
| 988 | 988 | } |
| 989 | - @ <br /><input type="submit" name="submit" value="Apply Changes" /> | |
| 989 | + @ <br><input type="submit" name="submit" value="Apply Changes"> | |
| 990 | 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | 991 | @ <table> |
| 992 | 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | ||
| 1012 | 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | 1016 | if( pSet->versionable ){ |
| 1017 | - @ (v)<br /> | |
| 1017 | + @ (v)<br> | |
| 1018 | 1018 | } else { |
| 1019 | - @ <br /> | |
| 1019 | + @ <br> | |
| 1020 | 1020 | } |
| 1021 | 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | - @<br /> | |
| 1024 | + @<br> | |
| 1025 | 1025 | } |
| 1026 | 1026 | } |
| 1027 | 1027 | @ </td></tr></table> |
| 1028 | 1028 | @ </div></form> |
| 1029 | 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | ||
| 1097 | 1097 | style_set_current_feature("setup"); |
| 1098 | 1098 | style_header("WWW Configuration"); |
| 1099 | 1099 | db_begin_transaction(); |
| 1100 | 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | 1101 | login_insert_csrf_secret(); |
| 1102 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1103 | - @ <hr /> | |
| 1102 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1103 | + @ <hr> | |
| 1104 | 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | 1107 | @ (Property: "project-name") |
| 1108 | 1108 | @ </p> |
| 1109 | - @ <hr /> | |
| 1109 | + @ <hr> | |
| 1110 | 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | 1111 | "project-description", "pd", "", 0); |
| 1112 | 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | 1113 | @ engines as well as a short RSS description. |
| 1114 | 1114 | @ (Property: "project-description")</p> |
| 1115 | - @ <hr /> | |
| 1115 | + @ <hr> | |
| 1116 | 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | 1117 | "eurl", "", 0); |
| 1118 | 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | ||
| 1130 | 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | 1131 | @ characters such as "/" and "\". |
| 1132 | 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | 1133 | @ (Property: "short-project-name") |
| 1134 | 1134 | @ </p> |
| 1135 | - @ <hr /> | |
| 1135 | + @ <hr> | |
| 1136 | 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | 1138 | @ a convenient place for newbies |
| 1139 | 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | 1142 | @ (Property: "download-tag") |
| 1143 | 1143 | @ </p> |
| 1144 | - @ <hr /> | |
| 1144 | + @ <hr> | |
| 1145 | 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | 1147 | @ option is selected and when no pathname is |
| 1148 | 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | 1149 | @ |
| @@ -1220,12 +1220,12 @@ | ||
| 1220 | 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | 1221 | @ (Property: sitemap-extra) |
| 1222 | 1222 | @ <p> |
| 1223 | 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | - @ <hr /> | |
| 1226 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1225 | + @ <hr> | |
| 1226 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1227 | 1227 | @ </div></form> |
| 1228 | 1228 | db_end_transaction(0); |
| 1229 | 1229 | style_finish_page(); |
| 1230 | 1230 | } |
| 1231 | 1231 | |
| @@ -1244,12 +1244,12 @@ | ||
| 1244 | 1244 | style_set_current_feature("setup"); |
| 1245 | 1245 | style_header("Wiki Configuration"); |
| 1246 | 1246 | db_begin_transaction(); |
| 1247 | 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | 1248 | login_insert_csrf_secret(); |
| 1249 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1250 | - @ <hr /> | |
| 1249 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1250 | + @ <hr> | |
| 1251 | 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | 1253 | @ <p> |
| 1254 | 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | ||
| 1259 | 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | 1262 | @ </ul> |
| 1263 | 1263 | @ (Property: "wiki-about")</p> |
| 1264 | - @ <hr /> | |
| 1264 | + @ <hr> | |
| 1265 | 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | ||
| 1279 | 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | 1282 | @ to generate unsafe HTML. |
| 1283 | 1283 | @ (Property: "safe-html")</p> |
| 1284 | - @ <hr /> | |
| 1284 | + @ <hr> | |
| 1285 | 1285 | @ The current interwiki tag map is as follows: |
| 1286 | 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | 1288 | @ modify the interwiki tag map. |
| 1289 | - @ <hr /> | |
| 1289 | + @ <hr> | |
| 1290 | 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | ||
| 1297 | 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | 1300 | @ editable wiki.</p> |
| 1301 | 1301 | @ (Property: "wiki-use-html") |
| 1302 | - @ <hr /> | |
| 1303 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1302 | + @ <hr> | |
| 1303 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1304 | 1304 | @ </div></form> |
| 1305 | 1305 | db_end_transaction(0); |
| 1306 | 1306 | style_finish_page(); |
| 1307 | 1307 | } |
| 1308 | 1308 | |
| @@ -1328,32 +1328,32 @@ | ||
| 1328 | 1328 | style_set_current_feature("setup"); |
| 1329 | 1329 | style_header("Chat Configuration"); |
| 1330 | 1330 | db_begin_transaction(); |
| 1331 | 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | 1332 | login_insert_csrf_secret(); |
| 1333 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1334 | - @ <hr /> | |
| 1333 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1334 | + @ <hr> | |
| 1335 | 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | 1338 | @ messages. |
| 1339 | 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | - @ <hr /> | |
| 1340 | + @ <hr> | |
| 1341 | 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | - @ <hr /> | |
| 1346 | + @ <hr> | |
| 1347 | 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | 1352 | @ to 0.5. |
| 1353 | 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | - @ <hr /> | |
| 1354 | + @ <hr> | |
| 1355 | 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | ||
| 1362 | 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | 1365 | @ should be larger rather than smaller. |
| 1366 | 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | - @ <hr /> | |
| 1367 | + @ <hr> | |
| 1368 | 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | 1373 | @ Suggested username: "chat-robot". |
| 1374 | 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | - @ <hr /> | |
| 1375 | + @ <hr> | |
| 1376 | 1376 | |
| 1377 | 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | 1381 | @ chat message has arrived. |
| 1382 | 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | 1383 | @ <hr/> |
| 1384 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1384 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1385 | 1385 | @ </div></form> |
| 1386 | 1386 | db_end_transaction(0); |
| 1387 | 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | 1388 | @ (function(){ |
| 1389 | 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | ||
| 1412 | 1412 | style_set_current_feature("setup"); |
| 1413 | 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | 1414 | db_begin_transaction(); |
| 1415 | 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | 1416 | login_insert_csrf_secret(); |
| 1417 | - @ <hr /> | |
| 1417 | + @ <hr> | |
| 1418 | 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | ||
| 1423 | 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | 1427 | @ |
| 1428 | - @ <hr /> | |
| 1428 | + @ <hr> | |
| 1429 | 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | ||
| 1435 | 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | 1438 | @ </p> |
| 1439 | 1439 | |
| 1440 | - @ <hr /> | |
| 1441 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1440 | + @ <hr> | |
| 1441 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1442 | 1442 | @ </div></form> |
| 1443 | 1443 | db_end_transaction(0); |
| 1444 | 1444 | style_finish_page(); |
| 1445 | 1445 | |
| 1446 | 1446 | } |
| @@ -1469,29 +1469,29 @@ | ||
| 1469 | 1469 | |
| 1470 | 1470 | style_set_current_feature("setup"); |
| 1471 | 1471 | style_header("Edit Ad Unit"); |
| 1472 | 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | 1473 | login_insert_csrf_secret(); |
| 1474 | - @ <b>Banner Ad-Unit:</b><br /> | |
| 1474 | + @ <b>Banner Ad-Unit:</b><br> | |
| 1475 | 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | - @ <br /> | |
| 1477 | - @ <b>Right-Column Ad-Unit:</b><br /> | |
| 1476 | + @ <br> | |
| 1477 | + @ <b>Right-Column Ad-Unit:</b><br> | |
| 1478 | 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | - @ <br /> | |
| 1479 | + @ <br> | |
| 1480 | 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | - @ <br /> | |
| 1482 | + @ <br> | |
| 1483 | 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | - @ <br /> | |
| 1485 | + @ <br> | |
| 1486 | 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | - @ <br /> | |
| 1489 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 1490 | - @ <input type="submit" name="clear" value="Delete Ad-Unit" /> | |
| 1488 | + @ <br> | |
| 1489 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 1490 | + @ <input type="submit" name="clear" value="Delete Ad-Unit"> | |
| 1491 | 1491 | @ </div></form> |
| 1492 | - @ <hr /> | |
| 1492 | + @ <hr> | |
| 1493 | 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | ||
| 1651 | 1651 | } |
| 1652 | 1652 | style_set_current_feature("setup"); |
| 1653 | 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | 1655 | @ and looks like this:</p> |
| 1656 | - @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \ | |
| 1657 | - @ alt="logo" border="1" /> | |
| 1656 | + @ <blockquote><p> | |
| 1657 | + @ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1"> | |
| 1658 | 1658 | @ </p></blockquote> |
| 1659 | 1659 | @ |
| 1660 | 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | ||
| 1665 | 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | 1668 | login_insert_csrf_secret(); |
| 1669 | 1669 | @ Logo Image file: |
| 1670 | - @ <input type="file" name="logoim" size="60" accept="image/*" /> | |
| 1670 | + @ <input type="file" name="logoim" size="60" accept="image/*"> | |
| 1671 | 1671 | @ <p align="center"> |
| 1672 | - @ <input type="submit" name="setlogo" value="Change Logo" /> | |
| 1673 | - @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> | |
| 1672 | + @ <input type="submit" name="setlogo" value="Change Logo"> | |
| 1673 | + @ <input type="submit" name="clrlogo" value="Revert To Default"></p> | |
| 1674 | 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | 1675 | @ </div></form> |
| 1676 | - @ <hr /> | |
| 1676 | + @ <hr> | |
| 1677 | 1677 | @ |
| 1678 | 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | 1679 | @ and looks like this:</p> |
| 1680 | 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | - @ alt="background" border=1 /> | |
| 1681 | + @ alt="background" border=1> | |
| 1682 | 1682 | @ </p></blockquote> |
| 1683 | 1683 | @ |
| 1684 | 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | ||
| 1689 | 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | 1691 | @ To change the background image, use the following form:</p> |
| 1692 | 1692 | login_insert_csrf_secret(); |
| 1693 | 1693 | @ Background image file: |
| 1694 | - @ <input type="file" name="bgim" size="60" accept="image/*" /> | |
| 1694 | + @ <input type="file" name="bgim" size="60" accept="image/*"> | |
| 1695 | 1695 | @ <p align="center"> |
| 1696 | - @ <input type="submit" name="setbg" value="Change Background" /> | |
| 1697 | - @ <input type="submit" name="clrbg" value="Revert To Default" /></p> | |
| 1696 | + @ <input type="submit" name="setbg" value="Change Background"> | |
| 1697 | + @ <input type="submit" name="clrbg" value="Revert To Default"></p> | |
| 1698 | 1698 | @ </div></form> |
| 1699 | 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | - @ <hr /> | |
| 1700 | + @ <hr> | |
| 1701 | 1701 | @ |
| 1702 | 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | 1703 | @ and looks like this:</p> |
| 1704 | 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | - @ alt="icon" border=1 /> | |
| 1705 | + @ alt="icon" border=1> | |
| 1706 | 1706 | @ </p></blockquote> |
| 1707 | 1707 | @ |
| 1708 | 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | ||
| 1713 | 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | 1714 | @ supports for icon images. |
| 1715 | 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | 1716 | login_insert_csrf_secret(); |
| 1717 | 1717 | @ Icon image file: |
| 1718 | - @ <input type="file" name="iconim" size="60" accept="image/*" /> | |
| 1718 | + @ <input type="file" name="iconim" size="60" accept="image/*"> | |
| 1719 | 1719 | @ <p align="center"> |
| 1720 | - @ <input type="submit" name="seticon" value="Change Icon" /> | |
| 1721 | - @ <input type="submit" name="clricon" value="Revert To Default" /></p> | |
| 1720 | + @ <input type="submit" name="seticon" value="Change Icon"> | |
| 1721 | + @ <input type="submit" name="clricon" value="Revert To Default"></p> | |
| 1722 | 1722 | @ </div></form> |
| 1723 | 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | - @ <hr /> | |
| 1724 | + @ <hr> | |
| 1725 | 1725 | @ |
| 1726 | 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | 1728 | @ take effect. </p> |
| 1729 | 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | ||
| 1804 | 1804 | go = 1; |
| 1805 | 1805 | } |
| 1806 | 1806 | @ |
| 1807 | 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | 1808 | login_insert_csrf_secret(); |
| 1809 | - @ SQL:<br /> | |
| 1810 | - @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br /> | |
| 1809 | + @ SQL:<br> | |
| 1810 | + @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br> | |
| 1811 | 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | ||
| 1827 | 1827 | int rc; |
| 1828 | 1828 | const char *zTail; |
| 1829 | 1829 | int nCol; |
| 1830 | 1830 | int nRow = 0; |
| 1831 | 1831 | int i; |
| 1832 | - @ <hr /> | |
| 1832 | + @ <hr> | |
| 1833 | 1833 | login_verify_csrf_secret(); |
| 1834 | 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | 1835 | search_sql_setup(g.db); |
| 1836 | 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | ||
| 1913 | 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | 1915 | @ |
| 1916 | 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | 1917 | login_insert_csrf_secret(); |
| 1918 | - @ TH1:<br /> | |
| 1919 | - @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br /> | |
| 1918 | + @ TH1:<br> | |
| 1919 | + @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> | |
| 1920 | 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | 1921 | @ </form> |
| 1922 | 1922 | if( go ){ |
| 1923 | 1923 | const char *zR; |
| 1924 | 1924 | int rc; |
| 1925 | 1925 | int n; |
| 1926 | - @ <hr /> | |
| 1926 | + @ <hr> | |
| 1927 | 1927 | login_verify_csrf_secret(); |
| 1928 | 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | 1930 | if( rc==TH_OK ){ |
| 1931 | 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | ||
| 2005 | 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | 2006 | } |
| 2007 | 2007 | style_finish_page(); |
| 2008 | 2008 | } |
| 2009 | 2009 | |
| 2010 | + | |
| 2011 | +/* | |
| 2012 | +** Renders a selection list of values for the search-tokenizer | |
| 2013 | +** setting, using the form field name "ftstok". | |
| 2014 | +*/ | |
| 2015 | +static void select_fts_tokenizer(void){ | |
| 2016 | + const char *const aTokenizer[] = { | |
| 2017 | + "off", "None", | |
| 2018 | + "porter", "Porter Stemmer", | |
| 2019 | + "trigram", "Trigram" | |
| 2020 | + }; | |
| 2021 | + multiple_choice_attribute("FTS Tokenizer", "search-tokenizer", | |
| 2022 | + "ftstok", "off", 3, aTokenizer); | |
| 2023 | +} | |
| 2024 | + | |
| 2010 | 2025 | /* |
| 2011 | 2026 | ** WEBPAGE: srchsetup |
| 2012 | 2027 | ** |
| 2013 | 2028 | ** Configure the search engine. Requires Admin privilege. |
| 2014 | 2029 | */ |
| @@ -2024,11 +2039,11 @@ | ||
| 2024 | 2039 | login_insert_csrf_secret(); |
| 2025 | 2040 | @ <div style="text-align:center;font-weight:bold;"> |
| 2026 | 2041 | @ Server-specific settings that affect the |
| 2027 | 2042 | @ <a href="%R/search">/search</a> webpage. |
| 2028 | 2043 | @ </div> |
| 2029 | - @ <hr /> | |
| 2044 | + @ <hr> | |
| 2030 | 2045 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2031 | 2046 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2032 | 2047 | @ of GLOB expressions that identify all documents within the source |
| 2033 | 2048 | @ tree that are to be searched when "Document Search" is enabled. |
| 2034 | 2049 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | ||
| 2040 | 2055 | @ files.</tr> |
| 2041 | 2056 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2042 | 2057 | @ <tr><td><i>(blank)</i><td> |
| 2043 | 2058 | @ <td>Search nothing. (Disables document search).</tr> |
| 2044 | 2059 | @ </table> |
| 2045 | - @ <hr /> | |
| 2060 | + @ <hr> | |
| 2046 | 2061 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2047 | 2062 | @ <p>When searching documents, use the versions of the files found at the |
| 2048 | 2063 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2049 | 2064 | @ Document search is disabled if blank. |
| 2050 | - @ <hr /> | |
| 2065 | + @ <hr> | |
| 2051 | 2066 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2052 | - @ <br /> | |
| 2067 | + @ <br> | |
| 2053 | 2068 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2054 | - @ <br /> | |
| 2069 | + @ <br> | |
| 2055 | 2070 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2056 | - @ <br /> | |
| 2071 | + @ <br> | |
| 2057 | 2072 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2058 | - @ <br /> | |
| 2073 | + @ <br> | |
| 2059 | 2074 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2060 | - @ <br /> | |
| 2075 | + @ <br> | |
| 2061 | 2076 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2062 | - @ <hr /> | |
| 2063 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 2064 | - @ <hr /> | |
| 2077 | + @ <hr> | |
| 2078 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 2079 | + @ <hr> | |
| 2065 | 2080 | if( P("fts0") ){ |
| 2066 | 2081 | search_drop_index(); |
| 2067 | 2082 | }else if( P("fts1") ){ |
| 2083 | + const char *zTokenizer = PD("ftstok","off"); | |
| 2084 | + search_set_tokenizer(zTokenizer); | |
| 2068 | 2085 | search_drop_index(); |
| 2069 | 2086 | search_create_index(); |
| 2070 | 2087 | search_fill_index(); |
| 2071 | 2088 | search_update_index(search_restrict(SRCH_ALL)); |
| 2072 | 2089 | } |
| 2073 | 2090 | if( search_index_exists() ){ |
| 2074 | 2091 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2075 | 2092 | @ The index helps search run faster, especially on large repositories, |
| 2076 | 2093 | @ but takes up space.</p> |
| 2077 | - onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); | |
| 2094 | + select_fts_tokenizer(); | |
| 2078 | 2095 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2079 | 2096 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2080 | 2097 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2081 | 2098 | }else{ |
| 2082 | 2099 | @ <p>The SQLite search index is disabled. All searching will be |
| 2083 | 2100 | @ a full-text scan. This usually works fine, but can be slow for |
| 2084 | 2101 | @ larger repositories.</p> |
| 2085 | - onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); | |
| 2102 | + select_fts_tokenizer(); | |
| 2086 | 2103 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2087 | 2104 | } |
| 2088 | 2105 | @ </div></form> |
| 2089 | 2106 | style_finish_page(); |
| 2090 | 2107 | } |
| 2091 | 2108 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -219,11 +219,11 @@ | |
| 219 | @ checked="checked" \ |
| 220 | } |
| 221 | if( disabled ){ |
| 222 | @ disabled="disabled" \ |
| 223 | } |
| 224 | @ /> <b>%s(zLabel)</b></label> |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | ** Generate an entry box for an attribute. |
| 229 | */ |
| @@ -251,11 +251,11 @@ | |
| 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | if( disabled ){ |
| 254 | @ disabled="disabled" \ |
| 255 | } |
| 256 | @ /> <b>%s(zLabel)</b> |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Generate a text box for an attribute. |
| 261 | */ |
| @@ -371,11 +371,11 @@ | |
| 371 | @ |
| 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | @ <blockquote> |
| 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | @ <br /> |
| 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | @ </blockquote> |
| 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | |
| 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | @ defend the site against robots. |
| 414 | @ |
| 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | login_insert_csrf_secret(); |
| 417 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 418 | @ <hr /> |
| 419 | addAutoHyperlinkSettings(); |
| 420 | @ <hr /> |
| 421 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 422 | @ </div></form> |
| 423 | db_end_transaction(0); |
| 424 | style_finish_page(); |
| 425 | } |
| 426 | |
| @@ -444,12 +444,12 @@ | |
| 444 | style_set_current_feature("setup"); |
| 445 | style_header("Access Control Settings"); |
| 446 | db_begin_transaction(); |
| 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | login_insert_csrf_secret(); |
| 449 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 450 | @ <hr /> |
| 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | |
| 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | @ "2" otherwise.) |
| 462 | @ <hr /> |
| 463 | onoff_attribute("Require password for local access", |
| 464 | "localauth", "localauth", 0, 0); |
| 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | |
| 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | @ in the CGI script. |
| 485 | @ </ol> |
| 486 | @ (Property: "localauth") |
| 487 | @ |
| 488 | @ <hr /> |
| 489 | onoff_attribute("Enable /test_env", |
| 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | @ the /test_env page. |
| 494 | @ (Property: "test_env_enable") |
| 495 | @ </p> |
| 496 | @ |
| 497 | @ <hr /> |
| 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | @ access the /artifact_stats page. |
| 503 | @ (Property: "artifact_stats_enable") |
| 504 | @ </p> |
| 505 | @ |
| 506 | @ <hr /> |
| 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | @ (Property: "remote_user_ok") |
| 513 | @ </p> |
| 514 | @ |
| 515 | @ <hr /> |
| 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | @ (Property: "http_authentication_ok") |
| 522 | @ </p> |
| 523 | @ |
| 524 | @ <hr /> |
| 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | "8766", 0); |
| 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | @ to a year. |
| 530 | @ (Property: "cookie-expire")</p> |
| 531 | |
| 532 | @ <hr /> |
| 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | "5000000", 0); |
| 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | |
| 541 | @ <hr /> |
| 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | "30", 0); |
| 544 | |
| 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | |
| 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | @ (Property: "max-download-time")</p> |
| 550 | |
| 551 | @ <a id="slal"></a> |
| 552 | @ <hr /> |
| 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | "0.0", 0); |
| 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | |
| 562 | @ (Property: "max-loadavg")</p> |
| 563 | |
| 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | ** are also accessible from the /setup_robot page. |
| 566 | */ |
| 567 | @ <hr /> |
| 568 | addAutoHyperlinkSettings(); |
| 569 | |
| 570 | @ <hr /> |
| 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | |
| 577 | @ <hr /> |
| 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | "pubpage", "", 0); |
| 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | @ without needing a login and using the privileges given by the |
| 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | |
| 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | @ allowing them to see the rest of the source code. |
| 589 | @ (Property: "public-pages") |
| 590 | @ </p> |
| 591 | |
| 592 | @ <hr /> |
| 593 | onoff_attribute("Allow users to register themselves", |
| 594 | "self-register", "selfreg", 0, 0); |
| 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | @ enabled. |
| 599 | @ (Property: "self-register")</p> |
| 600 | |
| 601 | @ <hr /> |
| 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | @ password reset page be sent to their email address of record. This |
| 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | @ administrator intervention. |
| 608 | @ (Property: "self-pw-reset")</p> |
| 609 | |
| 610 | @ <hr /> |
| 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | @ added until the email address associated with the self-registration |
| 616 | @ has been verified. This setting only makes sense if |
| 617 | @ email notifications are enabled. |
| 618 | @ (Property: "selfreg-verify")</p> |
| 619 | |
| 620 | @ <hr /> |
| 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | @ page, they are redirected to /register or /login. |
| 626 | @ (Property: "anon-subscribe")</p> |
| 627 | |
| 628 | @ <hr /> |
| 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | @ organization or group based on their email address. |
| 636 | @ (Property: "auth-sub-email")</p> |
| 637 | |
| 638 | @ <hr /> |
| 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | "defaultperms", "u", 0); |
| 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | |
| 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | @ (Property: "default-perms") |
| 648 | @ </p> |
| 649 | |
| 650 | @ <hr /> |
| 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | |
| 659 | @ <hr /> |
| 660 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 661 | @ </div></form> |
| 662 | db_end_transaction(0); |
| 663 | style_finish_page(); |
| 664 | } |
| 665 | |
| @@ -762,11 +762,11 @@ | |
| 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | login_insert_csrf_secret(); |
| 764 | @ To leave this login group press |
| 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | @ </form></p> |
| 767 | @ <hr /><h2>Implementation Details</h2> |
| 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | @ data-init-sort='1'> |
| 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | |
| 814 | style_set_current_feature("setup"); |
| 815 | style_header("Timeline Display Preferences"); |
| 816 | db_begin_transaction(); |
| 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | login_insert_csrf_secret(); |
| 819 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 820 | |
| 821 | @ <hr /> |
| 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | |
| 828 | @ <hr /> |
| 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | |
| 836 | @ <hr /> |
| 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | |
| 843 | @ <hr /> |
| 844 | onoff_attribute("Break comments at newline characters", |
| 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | @ a line break on the display. |
| 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | |
| 850 | @ <hr /> |
| 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | "timeline-utc", "utc", 1, 0); |
| 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | |
| 866 | }else{ |
| 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | } |
| 869 | @ <p>(Property: "timeline-utc") |
| 870 | |
| 871 | @ <hr /> |
| 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | |
| 877 | @ <hr /> |
| 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | "tldl", "50", 0); |
| 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | |
| 884 | @ <hr /> |
| 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | |
| 893 | @ <hr /> |
| 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | @ "0" there is no length limit. |
| 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | |
| 900 | @ <hr /> |
| 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | @ <br> |
| 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | |
| 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | |
| 916 | @ <hr /> |
| 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | |
| 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | @ right of the entry. |
| 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | |
| 932 | @ <hr /> |
| 933 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 934 | @ </div></form> |
| 935 | db_end_transaction(0); |
| 936 | style_finish_page(); |
| 937 | } |
| 938 | |
| @@ -964,11 +964,11 @@ | |
| 964 | db_begin_transaction(); |
| 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | @ by the contents of managed files named |
| 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | @ changed on this screen.</p><hr /><p> |
| 970 | @ |
| 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | login_insert_csrf_secret(); |
| 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | |
| 978 | onoff_attribute("", pSet->name, |
| 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | if( pSet->versionable ){ |
| 983 | @ (v)<br /> |
| 984 | } else { |
| 985 | @ <br /> |
| 986 | } |
| 987 | } |
| 988 | } |
| 989 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | @ <table> |
| 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | |
| 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | if( pSet->versionable ){ |
| 1017 | @ (v)<br /> |
| 1018 | } else { |
| 1019 | @ <br /> |
| 1020 | } |
| 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | @<br /> |
| 1025 | } |
| 1026 | } |
| 1027 | @ </td></tr></table> |
| 1028 | @ </div></form> |
| 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | |
| 1097 | style_set_current_feature("setup"); |
| 1098 | style_header("WWW Configuration"); |
| 1099 | db_begin_transaction(); |
| 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | login_insert_csrf_secret(); |
| 1102 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1103 | @ <hr /> |
| 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | @ (Property: "project-name") |
| 1108 | @ </p> |
| 1109 | @ <hr /> |
| 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | "project-description", "pd", "", 0); |
| 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | @ engines as well as a short RSS description. |
| 1114 | @ (Property: "project-description")</p> |
| 1115 | @ <hr /> |
| 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | "eurl", "", 0); |
| 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | |
| 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | @ characters such as "/" and "\". |
| 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | @ (Property: "short-project-name") |
| 1134 | @ </p> |
| 1135 | @ <hr /> |
| 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | @ a convenient place for newbies |
| 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | @ (Property: "download-tag") |
| 1143 | @ </p> |
| 1144 | @ <hr /> |
| 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | @ option is selected and when no pathname is |
| 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | @ |
| @@ -1220,12 +1220,12 @@ | |
| 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | @ (Property: sitemap-extra) |
| 1222 | @ <p> |
| 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | @ <hr /> |
| 1226 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1227 | @ </div></form> |
| 1228 | db_end_transaction(0); |
| 1229 | style_finish_page(); |
| 1230 | } |
| 1231 | |
| @@ -1244,12 +1244,12 @@ | |
| 1244 | style_set_current_feature("setup"); |
| 1245 | style_header("Wiki Configuration"); |
| 1246 | db_begin_transaction(); |
| 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | login_insert_csrf_secret(); |
| 1249 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1250 | @ <hr /> |
| 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | @ <p> |
| 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | |
| 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | @ </ul> |
| 1263 | @ (Property: "wiki-about")</p> |
| 1264 | @ <hr /> |
| 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | |
| 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | @ to generate unsafe HTML. |
| 1283 | @ (Property: "safe-html")</p> |
| 1284 | @ <hr /> |
| 1285 | @ The current interwiki tag map is as follows: |
| 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | @ modify the interwiki tag map. |
| 1289 | @ <hr /> |
| 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | |
| 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | @ editable wiki.</p> |
| 1301 | @ (Property: "wiki-use-html") |
| 1302 | @ <hr /> |
| 1303 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1304 | @ </div></form> |
| 1305 | db_end_transaction(0); |
| 1306 | style_finish_page(); |
| 1307 | } |
| 1308 | |
| @@ -1328,32 +1328,32 @@ | |
| 1328 | style_set_current_feature("setup"); |
| 1329 | style_header("Chat Configuration"); |
| 1330 | db_begin_transaction(); |
| 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | login_insert_csrf_secret(); |
| 1333 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1334 | @ <hr /> |
| 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | @ messages. |
| 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | @ <hr /> |
| 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | @ <hr /> |
| 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | @ to 0.5. |
| 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | @ <hr /> |
| 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | |
| 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | @ should be larger rather than smaller. |
| 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | @ <hr /> |
| 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | @ Suggested username: "chat-robot". |
| 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | @ <hr /> |
| 1376 | |
| 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | @ chat message has arrived. |
| 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | @ <hr/> |
| 1384 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1385 | @ </div></form> |
| 1386 | db_end_transaction(0); |
| 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | @ (function(){ |
| 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | style_set_current_feature("setup"); |
| 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | db_begin_transaction(); |
| 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | login_insert_csrf_secret(); |
| 1417 | @ <hr /> |
| 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | |
| 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | @ |
| 1428 | @ <hr /> |
| 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | |
| 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | @ </p> |
| 1439 | |
| 1440 | @ <hr /> |
| 1441 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1442 | @ </div></form> |
| 1443 | db_end_transaction(0); |
| 1444 | style_finish_page(); |
| 1445 | |
| 1446 | } |
| @@ -1469,29 +1469,29 @@ | |
| 1469 | |
| 1470 | style_set_current_feature("setup"); |
| 1471 | style_header("Edit Ad Unit"); |
| 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | login_insert_csrf_secret(); |
| 1474 | @ <b>Banner Ad-Unit:</b><br /> |
| 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | @ <br /> |
| 1477 | @ <b>Right-Column Ad-Unit:</b><br /> |
| 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | @ <br /> |
| 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | @ <br /> |
| 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | @ <br /> |
| 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | @ <br /> |
| 1489 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1490 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> |
| 1491 | @ </div></form> |
| 1492 | @ <hr /> |
| 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | |
| 1651 | } |
| 1652 | style_set_current_feature("setup"); |
| 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | @ and looks like this:</p> |
| 1656 | @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \ |
| 1657 | @ alt="logo" border="1" /> |
| 1658 | @ </p></blockquote> |
| 1659 | @ |
| 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | |
| 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | login_insert_csrf_secret(); |
| 1669 | @ Logo Image file: |
| 1670 | @ <input type="file" name="logoim" size="60" accept="image/*" /> |
| 1671 | @ <p align="center"> |
| 1672 | @ <input type="submit" name="setlogo" value="Change Logo" /> |
| 1673 | @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> |
| 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | @ </div></form> |
| 1676 | @ <hr /> |
| 1677 | @ |
| 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | @ and looks like this:</p> |
| 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | @ alt="background" border=1 /> |
| 1682 | @ </p></blockquote> |
| 1683 | @ |
| 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | |
| 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | @ To change the background image, use the following form:</p> |
| 1692 | login_insert_csrf_secret(); |
| 1693 | @ Background image file: |
| 1694 | @ <input type="file" name="bgim" size="60" accept="image/*" /> |
| 1695 | @ <p align="center"> |
| 1696 | @ <input type="submit" name="setbg" value="Change Background" /> |
| 1697 | @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
| 1698 | @ </div></form> |
| 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | @ <hr /> |
| 1701 | @ |
| 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | @ and looks like this:</p> |
| 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | @ alt="icon" border=1 /> |
| 1706 | @ </p></blockquote> |
| 1707 | @ |
| 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | |
| 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | @ supports for icon images. |
| 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | login_insert_csrf_secret(); |
| 1717 | @ Icon image file: |
| 1718 | @ <input type="file" name="iconim" size="60" accept="image/*" /> |
| 1719 | @ <p align="center"> |
| 1720 | @ <input type="submit" name="seticon" value="Change Icon" /> |
| 1721 | @ <input type="submit" name="clricon" value="Revert To Default" /></p> |
| 1722 | @ </div></form> |
| 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | @ <hr /> |
| 1725 | @ |
| 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | @ take effect. </p> |
| 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | |
| 1804 | go = 1; |
| 1805 | } |
| 1806 | @ |
| 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | login_insert_csrf_secret(); |
| 1809 | @ SQL:<br /> |
| 1810 | @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br /> |
| 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | |
| 1827 | int rc; |
| 1828 | const char *zTail; |
| 1829 | int nCol; |
| 1830 | int nRow = 0; |
| 1831 | int i; |
| 1832 | @ <hr /> |
| 1833 | login_verify_csrf_secret(); |
| 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | search_sql_setup(g.db); |
| 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | |
| 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | @ |
| 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | login_insert_csrf_secret(); |
| 1918 | @ TH1:<br /> |
| 1919 | @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br /> |
| 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | @ </form> |
| 1922 | if( go ){ |
| 1923 | const char *zR; |
| 1924 | int rc; |
| 1925 | int n; |
| 1926 | @ <hr /> |
| 1927 | login_verify_csrf_secret(); |
| 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | if( rc==TH_OK ){ |
| 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | |
| 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | } |
| 2007 | style_finish_page(); |
| 2008 | } |
| 2009 | |
| 2010 | /* |
| 2011 | ** WEBPAGE: srchsetup |
| 2012 | ** |
| 2013 | ** Configure the search engine. Requires Admin privilege. |
| 2014 | */ |
| @@ -2024,11 +2039,11 @@ | |
| 2024 | login_insert_csrf_secret(); |
| 2025 | @ <div style="text-align:center;font-weight:bold;"> |
| 2026 | @ Server-specific settings that affect the |
| 2027 | @ <a href="%R/search">/search</a> webpage. |
| 2028 | @ </div> |
| 2029 | @ <hr /> |
| 2030 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2031 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2032 | @ of GLOB expressions that identify all documents within the source |
| 2033 | @ tree that are to be searched when "Document Search" is enabled. |
| 2034 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | |
| 2040 | @ files.</tr> |
| 2041 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2042 | @ <tr><td><i>(blank)</i><td> |
| 2043 | @ <td>Search nothing. (Disables document search).</tr> |
| 2044 | @ </table> |
| 2045 | @ <hr /> |
| 2046 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2047 | @ <p>When searching documents, use the versions of the files found at the |
| 2048 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2049 | @ Document search is disabled if blank. |
| 2050 | @ <hr /> |
| 2051 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2052 | @ <br /> |
| 2053 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2054 | @ <br /> |
| 2055 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2056 | @ <br /> |
| 2057 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2058 | @ <br /> |
| 2059 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2060 | @ <br /> |
| 2061 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2062 | @ <hr /> |
| 2063 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 2064 | @ <hr /> |
| 2065 | if( P("fts0") ){ |
| 2066 | search_drop_index(); |
| 2067 | }else if( P("fts1") ){ |
| 2068 | search_drop_index(); |
| 2069 | search_create_index(); |
| 2070 | search_fill_index(); |
| 2071 | search_update_index(search_restrict(SRCH_ALL)); |
| 2072 | } |
| 2073 | if( search_index_exists() ){ |
| 2074 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2075 | @ The index helps search run faster, especially on large repositories, |
| 2076 | @ but takes up space.</p> |
| 2077 | onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); |
| 2078 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2079 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2080 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2081 | }else{ |
| 2082 | @ <p>The SQLite search index is disabled. All searching will be |
| 2083 | @ a full-text scan. This usually works fine, but can be slow for |
| 2084 | @ larger repositories.</p> |
| 2085 | onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); |
| 2086 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2087 | } |
| 2088 | @ </div></form> |
| 2089 | style_finish_page(); |
| 2090 | } |
| 2091 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -219,11 +219,11 @@ | |
| 219 | @ checked="checked" \ |
| 220 | } |
| 221 | if( disabled ){ |
| 222 | @ disabled="disabled" \ |
| 223 | } |
| 224 | @ > <b>%s(zLabel)</b></label> |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | ** Generate an entry box for an attribute. |
| 229 | */ |
| @@ -251,11 +251,11 @@ | |
| 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | if( disabled ){ |
| 254 | @ disabled="disabled" \ |
| 255 | } |
| 256 | @ > <b>%s(zLabel)</b> |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Generate a text box for an attribute. |
| 261 | */ |
| @@ -371,11 +371,11 @@ | |
| 371 | @ |
| 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | @ <blockquote> |
| 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | @ <br> |
| 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | @ </blockquote> |
| 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | |
| 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | @ defend the site against robots. |
| 414 | @ |
| 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | login_insert_csrf_secret(); |
| 417 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 418 | @ <hr> |
| 419 | addAutoHyperlinkSettings(); |
| 420 | @ <hr> |
| 421 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 422 | @ </div></form> |
| 423 | db_end_transaction(0); |
| 424 | style_finish_page(); |
| 425 | } |
| 426 | |
| @@ -444,12 +444,12 @@ | |
| 444 | style_set_current_feature("setup"); |
| 445 | style_header("Access Control Settings"); |
| 446 | db_begin_transaction(); |
| 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | login_insert_csrf_secret(); |
| 449 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 450 | @ <hr> |
| 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | |
| 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | @ "2" otherwise.) |
| 462 | @ <hr> |
| 463 | onoff_attribute("Require password for local access", |
| 464 | "localauth", "localauth", 0, 0); |
| 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | |
| 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | @ in the CGI script. |
| 485 | @ </ol> |
| 486 | @ (Property: "localauth") |
| 487 | @ |
| 488 | @ <hr> |
| 489 | onoff_attribute("Enable /test_env", |
| 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | @ the /test_env page. |
| 494 | @ (Property: "test_env_enable") |
| 495 | @ </p> |
| 496 | @ |
| 497 | @ <hr> |
| 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | @ access the /artifact_stats page. |
| 503 | @ (Property: "artifact_stats_enable") |
| 504 | @ </p> |
| 505 | @ |
| 506 | @ <hr> |
| 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | @ (Property: "remote_user_ok") |
| 513 | @ </p> |
| 514 | @ |
| 515 | @ <hr> |
| 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | @ (Property: "http_authentication_ok") |
| 522 | @ </p> |
| 523 | @ |
| 524 | @ <hr> |
| 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | "8766", 0); |
| 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | @ to a year. |
| 530 | @ (Property: "cookie-expire")</p> |
| 531 | |
| 532 | @ <hr> |
| 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | "5000000", 0); |
| 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | |
| 541 | @ <hr> |
| 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | "30", 0); |
| 544 | |
| 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | |
| 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | @ (Property: "max-download-time")</p> |
| 550 | |
| 551 | @ <a id="slal"></a> |
| 552 | @ <hr> |
| 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | "0.0", 0); |
| 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | |
| 562 | @ (Property: "max-loadavg")</p> |
| 563 | |
| 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | ** are also accessible from the /setup_robot page. |
| 566 | */ |
| 567 | @ <hr> |
| 568 | addAutoHyperlinkSettings(); |
| 569 | |
| 570 | @ <hr> |
| 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | |
| 577 | @ <hr> |
| 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | "pubpage", "", 0); |
| 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | @ without needing a login and using the privileges given by the |
| 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | |
| 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | @ allowing them to see the rest of the source code. |
| 589 | @ (Property: "public-pages") |
| 590 | @ </p> |
| 591 | |
| 592 | @ <hr> |
| 593 | onoff_attribute("Allow users to register themselves", |
| 594 | "self-register", "selfreg", 0, 0); |
| 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | @ enabled. |
| 599 | @ (Property: "self-register")</p> |
| 600 | |
| 601 | @ <hr> |
| 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | @ password reset page be sent to their email address of record. This |
| 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | @ administrator intervention. |
| 608 | @ (Property: "self-pw-reset")</p> |
| 609 | |
| 610 | @ <hr> |
| 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | @ added until the email address associated with the self-registration |
| 616 | @ has been verified. This setting only makes sense if |
| 617 | @ email notifications are enabled. |
| 618 | @ (Property: "selfreg-verify")</p> |
| 619 | |
| 620 | @ <hr> |
| 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | @ page, they are redirected to /register or /login. |
| 626 | @ (Property: "anon-subscribe")</p> |
| 627 | |
| 628 | @ <hr> |
| 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | @ organization or group based on their email address. |
| 636 | @ (Property: "auth-sub-email")</p> |
| 637 | |
| 638 | @ <hr> |
| 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | "defaultperms", "u", 0); |
| 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | |
| 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | @ (Property: "default-perms") |
| 648 | @ </p> |
| 649 | |
| 650 | @ <hr> |
| 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | |
| 659 | @ <hr> |
| 660 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 661 | @ </div></form> |
| 662 | db_end_transaction(0); |
| 663 | style_finish_page(); |
| 664 | } |
| 665 | |
| @@ -762,11 +762,11 @@ | |
| 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | login_insert_csrf_secret(); |
| 764 | @ To leave this login group press |
| 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | @ </form></p> |
| 767 | @ <hr><h2>Implementation Details</h2> |
| 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | @ data-init-sort='1'> |
| 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | |
| 814 | style_set_current_feature("setup"); |
| 815 | style_header("Timeline Display Preferences"); |
| 816 | db_begin_transaction(); |
| 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | login_insert_csrf_secret(); |
| 819 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 820 | |
| 821 | @ <hr> |
| 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | |
| 828 | @ <hr> |
| 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | |
| 836 | @ <hr> |
| 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | |
| 843 | @ <hr> |
| 844 | onoff_attribute("Break comments at newline characters", |
| 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | @ a line break on the display. |
| 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | |
| 850 | @ <hr> |
| 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | "timeline-utc", "utc", 1, 0); |
| 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | |
| 866 | }else{ |
| 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | } |
| 869 | @ <p>(Property: "timeline-utc") |
| 870 | |
| 871 | @ <hr> |
| 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | |
| 877 | @ <hr> |
| 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | "tldl", "50", 0); |
| 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | |
| 884 | @ <hr> |
| 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | |
| 893 | @ <hr> |
| 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | @ "0" there is no length limit. |
| 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | |
| 900 | @ <hr> |
| 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | @ <br> |
| 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | |
| 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | |
| 916 | @ <hr> |
| 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | |
| 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | @ right of the entry. |
| 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | |
| 932 | @ <hr> |
| 933 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 934 | @ </div></form> |
| 935 | db_end_transaction(0); |
| 936 | style_finish_page(); |
| 937 | } |
| 938 | |
| @@ -964,11 +964,11 @@ | |
| 964 | db_begin_transaction(); |
| 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | @ by the contents of managed files named |
| 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | @ changed on this screen.</p><hr><p> |
| 970 | @ |
| 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | login_insert_csrf_secret(); |
| 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | |
| 978 | onoff_attribute("", pSet->name, |
| 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | if( pSet->versionable ){ |
| 983 | @ (v)<br> |
| 984 | } else { |
| 985 | @ <br> |
| 986 | } |
| 987 | } |
| 988 | } |
| 989 | @ <br><input type="submit" name="submit" value="Apply Changes"> |
| 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | @ <table> |
| 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | |
| 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | if( pSet->versionable ){ |
| 1017 | @ (v)<br> |
| 1018 | } else { |
| 1019 | @ <br> |
| 1020 | } |
| 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | @<br> |
| 1025 | } |
| 1026 | } |
| 1027 | @ </td></tr></table> |
| 1028 | @ </div></form> |
| 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | |
| 1097 | style_set_current_feature("setup"); |
| 1098 | style_header("WWW Configuration"); |
| 1099 | db_begin_transaction(); |
| 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | login_insert_csrf_secret(); |
| 1102 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1103 | @ <hr> |
| 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | @ (Property: "project-name") |
| 1108 | @ </p> |
| 1109 | @ <hr> |
| 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | "project-description", "pd", "", 0); |
| 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | @ engines as well as a short RSS description. |
| 1114 | @ (Property: "project-description")</p> |
| 1115 | @ <hr> |
| 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | "eurl", "", 0); |
| 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | |
| 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | @ characters such as "/" and "\". |
| 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | @ (Property: "short-project-name") |
| 1134 | @ </p> |
| 1135 | @ <hr> |
| 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | @ a convenient place for newbies |
| 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | @ (Property: "download-tag") |
| 1143 | @ </p> |
| 1144 | @ <hr> |
| 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | @ option is selected and when no pathname is |
| 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | @ |
| @@ -1220,12 +1220,12 @@ | |
| 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | @ (Property: sitemap-extra) |
| 1222 | @ <p> |
| 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | @ <hr> |
| 1226 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1227 | @ </div></form> |
| 1228 | db_end_transaction(0); |
| 1229 | style_finish_page(); |
| 1230 | } |
| 1231 | |
| @@ -1244,12 +1244,12 @@ | |
| 1244 | style_set_current_feature("setup"); |
| 1245 | style_header("Wiki Configuration"); |
| 1246 | db_begin_transaction(); |
| 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | login_insert_csrf_secret(); |
| 1249 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1250 | @ <hr> |
| 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | @ <p> |
| 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | |
| 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | @ </ul> |
| 1263 | @ (Property: "wiki-about")</p> |
| 1264 | @ <hr> |
| 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | |
| 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | @ to generate unsafe HTML. |
| 1283 | @ (Property: "safe-html")</p> |
| 1284 | @ <hr> |
| 1285 | @ The current interwiki tag map is as follows: |
| 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | @ modify the interwiki tag map. |
| 1289 | @ <hr> |
| 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | |
| 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | @ editable wiki.</p> |
| 1301 | @ (Property: "wiki-use-html") |
| 1302 | @ <hr> |
| 1303 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1304 | @ </div></form> |
| 1305 | db_end_transaction(0); |
| 1306 | style_finish_page(); |
| 1307 | } |
| 1308 | |
| @@ -1328,32 +1328,32 @@ | |
| 1328 | style_set_current_feature("setup"); |
| 1329 | style_header("Chat Configuration"); |
| 1330 | db_begin_transaction(); |
| 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | login_insert_csrf_secret(); |
| 1333 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1334 | @ <hr> |
| 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | @ messages. |
| 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | @ <hr> |
| 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | @ <hr> |
| 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | @ to 0.5. |
| 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | @ <hr> |
| 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | |
| 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | @ should be larger rather than smaller. |
| 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | @ <hr> |
| 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | @ Suggested username: "chat-robot". |
| 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | @ <hr> |
| 1376 | |
| 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | @ chat message has arrived. |
| 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | @ <hr/> |
| 1384 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1385 | @ </div></form> |
| 1386 | db_end_transaction(0); |
| 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | @ (function(){ |
| 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | style_set_current_feature("setup"); |
| 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | db_begin_transaction(); |
| 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | login_insert_csrf_secret(); |
| 1417 | @ <hr> |
| 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | |
| 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | @ |
| 1428 | @ <hr> |
| 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | |
| 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | @ </p> |
| 1439 | |
| 1440 | @ <hr> |
| 1441 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1442 | @ </div></form> |
| 1443 | db_end_transaction(0); |
| 1444 | style_finish_page(); |
| 1445 | |
| 1446 | } |
| @@ -1469,29 +1469,29 @@ | |
| 1469 | |
| 1470 | style_set_current_feature("setup"); |
| 1471 | style_header("Edit Ad Unit"); |
| 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | login_insert_csrf_secret(); |
| 1474 | @ <b>Banner Ad-Unit:</b><br> |
| 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | @ <br> |
| 1477 | @ <b>Right-Column Ad-Unit:</b><br> |
| 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | @ <br> |
| 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | @ <br> |
| 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | @ <br> |
| 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | @ <br> |
| 1489 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 1490 | @ <input type="submit" name="clear" value="Delete Ad-Unit"> |
| 1491 | @ </div></form> |
| 1492 | @ <hr> |
| 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | |
| 1651 | } |
| 1652 | style_set_current_feature("setup"); |
| 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | @ and looks like this:</p> |
| 1656 | @ <blockquote><p> |
| 1657 | @ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1"> |
| 1658 | @ </p></blockquote> |
| 1659 | @ |
| 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | |
| 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | login_insert_csrf_secret(); |
| 1669 | @ Logo Image file: |
| 1670 | @ <input type="file" name="logoim" size="60" accept="image/*"> |
| 1671 | @ <p align="center"> |
| 1672 | @ <input type="submit" name="setlogo" value="Change Logo"> |
| 1673 | @ <input type="submit" name="clrlogo" value="Revert To Default"></p> |
| 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | @ </div></form> |
| 1676 | @ <hr> |
| 1677 | @ |
| 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | @ and looks like this:</p> |
| 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | @ alt="background" border=1> |
| 1682 | @ </p></blockquote> |
| 1683 | @ |
| 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | |
| 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | @ To change the background image, use the following form:</p> |
| 1692 | login_insert_csrf_secret(); |
| 1693 | @ Background image file: |
| 1694 | @ <input type="file" name="bgim" size="60" accept="image/*"> |
| 1695 | @ <p align="center"> |
| 1696 | @ <input type="submit" name="setbg" value="Change Background"> |
| 1697 | @ <input type="submit" name="clrbg" value="Revert To Default"></p> |
| 1698 | @ </div></form> |
| 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | @ <hr> |
| 1701 | @ |
| 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | @ and looks like this:</p> |
| 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | @ alt="icon" border=1> |
| 1706 | @ </p></blockquote> |
| 1707 | @ |
| 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | |
| 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | @ supports for icon images. |
| 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | login_insert_csrf_secret(); |
| 1717 | @ Icon image file: |
| 1718 | @ <input type="file" name="iconim" size="60" accept="image/*"> |
| 1719 | @ <p align="center"> |
| 1720 | @ <input type="submit" name="seticon" value="Change Icon"> |
| 1721 | @ <input type="submit" name="clricon" value="Revert To Default"></p> |
| 1722 | @ </div></form> |
| 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | @ <hr> |
| 1725 | @ |
| 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | @ take effect. </p> |
| 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | |
| 1804 | go = 1; |
| 1805 | } |
| 1806 | @ |
| 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | login_insert_csrf_secret(); |
| 1809 | @ SQL:<br> |
| 1810 | @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br> |
| 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | |
| 1827 | int rc; |
| 1828 | const char *zTail; |
| 1829 | int nCol; |
| 1830 | int nRow = 0; |
| 1831 | int i; |
| 1832 | @ <hr> |
| 1833 | login_verify_csrf_secret(); |
| 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | search_sql_setup(g.db); |
| 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | |
| 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | @ |
| 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | login_insert_csrf_secret(); |
| 1918 | @ TH1:<br> |
| 1919 | @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> |
| 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | @ </form> |
| 1922 | if( go ){ |
| 1923 | const char *zR; |
| 1924 | int rc; |
| 1925 | int n; |
| 1926 | @ <hr> |
| 1927 | login_verify_csrf_secret(); |
| 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | if( rc==TH_OK ){ |
| 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | |
| 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | } |
| 2007 | style_finish_page(); |
| 2008 | } |
| 2009 | |
| 2010 | |
| 2011 | /* |
| 2012 | ** Renders a selection list of values for the search-tokenizer |
| 2013 | ** setting, using the form field name "ftstok". |
| 2014 | */ |
| 2015 | static void select_fts_tokenizer(void){ |
| 2016 | const char *const aTokenizer[] = { |
| 2017 | "off", "None", |
| 2018 | "porter", "Porter Stemmer", |
| 2019 | "trigram", "Trigram" |
| 2020 | }; |
| 2021 | multiple_choice_attribute("FTS Tokenizer", "search-tokenizer", |
| 2022 | "ftstok", "off", 3, aTokenizer); |
| 2023 | } |
| 2024 | |
| 2025 | /* |
| 2026 | ** WEBPAGE: srchsetup |
| 2027 | ** |
| 2028 | ** Configure the search engine. Requires Admin privilege. |
| 2029 | */ |
| @@ -2024,11 +2039,11 @@ | |
| 2039 | login_insert_csrf_secret(); |
| 2040 | @ <div style="text-align:center;font-weight:bold;"> |
| 2041 | @ Server-specific settings that affect the |
| 2042 | @ <a href="%R/search">/search</a> webpage. |
| 2043 | @ </div> |
| 2044 | @ <hr> |
| 2045 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2046 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2047 | @ of GLOB expressions that identify all documents within the source |
| 2048 | @ tree that are to be searched when "Document Search" is enabled. |
| 2049 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | |
| 2055 | @ files.</tr> |
| 2056 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2057 | @ <tr><td><i>(blank)</i><td> |
| 2058 | @ <td>Search nothing. (Disables document search).</tr> |
| 2059 | @ </table> |
| 2060 | @ <hr> |
| 2061 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2062 | @ <p>When searching documents, use the versions of the files found at the |
| 2063 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2064 | @ Document search is disabled if blank. |
| 2065 | @ <hr> |
| 2066 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2067 | @ <br> |
| 2068 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2069 | @ <br> |
| 2070 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2071 | @ <br> |
| 2072 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2073 | @ <br> |
| 2074 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2075 | @ <br> |
| 2076 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2077 | @ <hr> |
| 2078 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 2079 | @ <hr> |
| 2080 | if( P("fts0") ){ |
| 2081 | search_drop_index(); |
| 2082 | }else if( P("fts1") ){ |
| 2083 | const char *zTokenizer = PD("ftstok","off"); |
| 2084 | search_set_tokenizer(zTokenizer); |
| 2085 | search_drop_index(); |
| 2086 | search_create_index(); |
| 2087 | search_fill_index(); |
| 2088 | search_update_index(search_restrict(SRCH_ALL)); |
| 2089 | } |
| 2090 | if( search_index_exists() ){ |
| 2091 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2092 | @ The index helps search run faster, especially on large repositories, |
| 2093 | @ but takes up space.</p> |
| 2094 | select_fts_tokenizer(); |
| 2095 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2096 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2097 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2098 | }else{ |
| 2099 | @ <p>The SQLite search index is disabled. All searching will be |
| 2100 | @ a full-text scan. This usually works fine, but can be slow for |
| 2101 | @ larger repositories.</p> |
| 2102 | select_fts_tokenizer(); |
| 2103 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2104 | } |
| 2105 | @ </div></form> |
| 2106 | style_finish_page(); |
| 2107 | } |
| 2108 |
+141
-124
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -219,11 +219,11 @@ | ||
| 219 | 219 | @ checked="checked" \ |
| 220 | 220 | } |
| 221 | 221 | if( disabled ){ |
| 222 | 222 | @ disabled="disabled" \ |
| 223 | 223 | } |
| 224 | - @ /> <b>%s(zLabel)</b></label> | |
| 224 | + @ > <b>%s(zLabel)</b></label> | |
| 225 | 225 | } |
| 226 | 226 | |
| 227 | 227 | /* |
| 228 | 228 | ** Generate an entry box for an attribute. |
| 229 | 229 | */ |
| @@ -251,11 +251,11 @@ | ||
| 251 | 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | 253 | if( disabled ){ |
| 254 | 254 | @ disabled="disabled" \ |
| 255 | 255 | } |
| 256 | - @ /> <b>%s(zLabel)</b> | |
| 256 | + @ > <b>%s(zLabel)</b> | |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | 259 | /* |
| 260 | 260 | ** Generate a text box for an attribute. |
| 261 | 261 | */ |
| @@ -371,11 +371,11 @@ | ||
| 371 | 371 | @ |
| 372 | 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | 373 | @ <blockquote> |
| 374 | 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | - @ <br /> | |
| 376 | + @ <br> | |
| 377 | 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | 379 | @ </blockquote> |
| 380 | 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | ||
| 412 | 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | 413 | @ defend the site against robots. |
| 414 | 414 | @ |
| 415 | 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | 416 | login_insert_csrf_secret(); |
| 417 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 418 | - @ <hr /> | |
| 417 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 418 | + @ <hr> | |
| 419 | 419 | addAutoHyperlinkSettings(); |
| 420 | - @ <hr /> | |
| 421 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 420 | + @ <hr> | |
| 421 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 422 | 422 | @ </div></form> |
| 423 | 423 | db_end_transaction(0); |
| 424 | 424 | style_finish_page(); |
| 425 | 425 | } |
| 426 | 426 | |
| @@ -444,12 +444,12 @@ | ||
| 444 | 444 | style_set_current_feature("setup"); |
| 445 | 445 | style_header("Access Control Settings"); |
| 446 | 446 | db_begin_transaction(); |
| 447 | 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | 448 | login_insert_csrf_secret(); |
| 449 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 450 | - @ <hr /> | |
| 449 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 450 | + @ <hr> | |
| 451 | 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | ||
| 457 | 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | 461 | @ "2" otherwise.) |
| 462 | - @ <hr /> | |
| 462 | + @ <hr> | |
| 463 | 463 | onoff_attribute("Require password for local access", |
| 464 | 464 | "localauth", "localauth", 0, 0); |
| 465 | 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | ||
| 483 | 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | 484 | @ in the CGI script. |
| 485 | 485 | @ </ol> |
| 486 | 486 | @ (Property: "localauth") |
| 487 | 487 | @ |
| 488 | - @ <hr /> | |
| 488 | + @ <hr> | |
| 489 | 489 | onoff_attribute("Enable /test_env", |
| 490 | 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | 493 | @ the /test_env page. |
| 494 | 494 | @ (Property: "test_env_enable") |
| 495 | 495 | @ </p> |
| 496 | 496 | @ |
| 497 | - @ <hr /> | |
| 497 | + @ <hr> | |
| 498 | 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | 502 | @ access the /artifact_stats page. |
| 503 | 503 | @ (Property: "artifact_stats_enable") |
| 504 | 504 | @ </p> |
| 505 | 505 | @ |
| 506 | - @ <hr /> | |
| 506 | + @ <hr> | |
| 507 | 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | 512 | @ (Property: "remote_user_ok") |
| 513 | 513 | @ </p> |
| 514 | 514 | @ |
| 515 | - @ <hr /> | |
| 515 | + @ <hr> | |
| 516 | 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | 521 | @ (Property: "http_authentication_ok") |
| 522 | 522 | @ </p> |
| 523 | 523 | @ |
| 524 | - @ <hr /> | |
| 524 | + @ <hr> | |
| 525 | 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | 526 | "8766", 0); |
| 527 | 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | 529 | @ to a year. |
| 530 | 530 | @ (Property: "cookie-expire")</p> |
| 531 | 531 | |
| 532 | - @ <hr /> | |
| 532 | + @ <hr> | |
| 533 | 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | 534 | "5000000", 0); |
| 535 | 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | 540 | |
| 541 | - @ <hr /> | |
| 541 | + @ <hr> | |
| 542 | 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | 543 | "30", 0); |
| 544 | 544 | |
| 545 | 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | ||
| 547 | 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | 549 | @ (Property: "max-download-time")</p> |
| 550 | 550 | |
| 551 | 551 | @ <a id="slal"></a> |
| 552 | - @ <hr /> | |
| 552 | + @ <hr> | |
| 553 | 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | 554 | "0.0", 0); |
| 555 | 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | ||
| 562 | 562 | @ (Property: "max-loadavg")</p> |
| 563 | 563 | |
| 564 | 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | 565 | ** are also accessible from the /setup_robot page. |
| 566 | 566 | */ |
| 567 | - @ <hr /> | |
| 567 | + @ <hr> | |
| 568 | 568 | addAutoHyperlinkSettings(); |
| 569 | 569 | |
| 570 | - @ <hr /> | |
| 570 | + @ <hr> | |
| 571 | 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | 576 | |
| 577 | - @ <hr /> | |
| 577 | + @ <hr> | |
| 578 | 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | 579 | "pubpage", "", 0); |
| 580 | 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | 581 | @ without needing a login and using the privileges given by the |
| 582 | 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | ||
| 587 | 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | 588 | @ allowing them to see the rest of the source code. |
| 589 | 589 | @ (Property: "public-pages") |
| 590 | 590 | @ </p> |
| 591 | 591 | |
| 592 | - @ <hr /> | |
| 592 | + @ <hr> | |
| 593 | 593 | onoff_attribute("Allow users to register themselves", |
| 594 | 594 | "self-register", "selfreg", 0, 0); |
| 595 | 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | 598 | @ enabled. |
| 599 | 599 | @ (Property: "self-register")</p> |
| 600 | 600 | |
| 601 | - @ <hr /> | |
| 601 | + @ <hr> | |
| 602 | 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | 605 | @ password reset page be sent to their email address of record. This |
| 606 | 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | 607 | @ administrator intervention. |
| 608 | 608 | @ (Property: "self-pw-reset")</p> |
| 609 | 609 | |
| 610 | - @ <hr /> | |
| 610 | + @ <hr> | |
| 611 | 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | 615 | @ added until the email address associated with the self-registration |
| 616 | 616 | @ has been verified. This setting only makes sense if |
| 617 | 617 | @ email notifications are enabled. |
| 618 | 618 | @ (Property: "selfreg-verify")</p> |
| 619 | 619 | |
| 620 | - @ <hr /> | |
| 620 | + @ <hr> | |
| 621 | 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | 625 | @ page, they are redirected to /register or /login. |
| 626 | 626 | @ (Property: "anon-subscribe")</p> |
| 627 | 627 | |
| 628 | - @ <hr /> | |
| 628 | + @ <hr> | |
| 629 | 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | 635 | @ organization or group based on their email address. |
| 636 | 636 | @ (Property: "auth-sub-email")</p> |
| 637 | 637 | |
| 638 | - @ <hr /> | |
| 638 | + @ <hr> | |
| 639 | 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | 640 | "defaultperms", "u", 0); |
| 641 | 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | ||
| 645 | 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | 647 | @ (Property: "default-perms") |
| 648 | 648 | @ </p> |
| 649 | 649 | |
| 650 | - @ <hr /> | |
| 650 | + @ <hr> | |
| 651 | 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | 658 | |
| 659 | - @ <hr /> | |
| 660 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 659 | + @ <hr> | |
| 660 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 661 | 661 | @ </div></form> |
| 662 | 662 | db_end_transaction(0); |
| 663 | 663 | style_finish_page(); |
| 664 | 664 | } |
| 665 | 665 | |
| @@ -762,11 +762,11 @@ | ||
| 762 | 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | 763 | login_insert_csrf_secret(); |
| 764 | 764 | @ To leave this login group press |
| 765 | 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | 766 | @ </form></p> |
| 767 | - @ <hr /><h2>Implementation Details</h2> | |
| 767 | + @ <hr><h2>Implementation Details</h2> | |
| 768 | 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | 771 | @ data-init-sort='1'> |
| 772 | 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | ||
| 814 | 814 | style_set_current_feature("setup"); |
| 815 | 815 | style_header("Timeline Display Preferences"); |
| 816 | 816 | db_begin_transaction(); |
| 817 | 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | 818 | login_insert_csrf_secret(); |
| 819 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 819 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 820 | 820 | |
| 821 | - @ <hr /> | |
| 821 | + @ <hr> | |
| 822 | 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | 827 | |
| 828 | - @ <hr /> | |
| 828 | + @ <hr> | |
| 829 | 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | 835 | |
| 836 | - @ <hr /> | |
| 836 | + @ <hr> | |
| 837 | 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | 842 | |
| 843 | - @ <hr /> | |
| 843 | + @ <hr> | |
| 844 | 844 | onoff_attribute("Break comments at newline characters", |
| 845 | 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | 847 | @ a line break on the display. |
| 848 | 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | 849 | |
| 850 | - @ <hr /> | |
| 850 | + @ <hr> | |
| 851 | 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | 852 | "timeline-utc", "utc", 1, 0); |
| 853 | 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | ||
| 866 | 866 | }else{ |
| 867 | 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | 868 | } |
| 869 | 869 | @ <p>(Property: "timeline-utc") |
| 870 | 870 | |
| 871 | - @ <hr /> | |
| 871 | + @ <hr> | |
| 872 | 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | 876 | |
| 877 | - @ <hr /> | |
| 877 | + @ <hr> | |
| 878 | 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | 879 | "tldl", "50", 0); |
| 880 | 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | 883 | |
| 884 | - @ <hr /> | |
| 884 | + @ <hr> | |
| 885 | 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | 892 | |
| 893 | - @ <hr /> | |
| 893 | + @ <hr> | |
| 894 | 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | 897 | @ "0" there is no length limit. |
| 898 | 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | 899 | |
| 900 | - @ <hr /> | |
| 900 | + @ <hr> | |
| 901 | 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | 903 | @ <br> |
| 904 | 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | ||
| 911 | 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | 915 | |
| 916 | - @ <hr /> | |
| 916 | + @ <hr> | |
| 917 | 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | ||
| 927 | 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | 929 | @ right of the entry. |
| 930 | 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | 931 | |
| 932 | - @ <hr /> | |
| 933 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 932 | + @ <hr> | |
| 933 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 934 | 934 | @ </div></form> |
| 935 | 935 | db_end_transaction(0); |
| 936 | 936 | style_finish_page(); |
| 937 | 937 | } |
| 938 | 938 | |
| @@ -964,11 +964,11 @@ | ||
| 964 | 964 | db_begin_transaction(); |
| 965 | 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | 966 | @ by the contents of managed files named |
| 967 | 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | - @ changed on this screen.</p><hr /><p> | |
| 969 | + @ changed on this screen.</p><hr><p> | |
| 970 | 970 | @ |
| 971 | 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | 973 | login_insert_csrf_secret(); |
| 974 | 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | ||
| 978 | 978 | onoff_attribute("", pSet->name, |
| 979 | 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | 982 | if( pSet->versionable ){ |
| 983 | - @ (v)<br /> | |
| 983 | + @ (v)<br> | |
| 984 | 984 | } else { |
| 985 | - @ <br /> | |
| 985 | + @ <br> | |
| 986 | 986 | } |
| 987 | 987 | } |
| 988 | 988 | } |
| 989 | - @ <br /><input type="submit" name="submit" value="Apply Changes" /> | |
| 989 | + @ <br><input type="submit" name="submit" value="Apply Changes"> | |
| 990 | 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | 991 | @ <table> |
| 992 | 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | ||
| 1012 | 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | 1016 | if( pSet->versionable ){ |
| 1017 | - @ (v)<br /> | |
| 1017 | + @ (v)<br> | |
| 1018 | 1018 | } else { |
| 1019 | - @ <br /> | |
| 1019 | + @ <br> | |
| 1020 | 1020 | } |
| 1021 | 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | - @<br /> | |
| 1024 | + @<br> | |
| 1025 | 1025 | } |
| 1026 | 1026 | } |
| 1027 | 1027 | @ </td></tr></table> |
| 1028 | 1028 | @ </div></form> |
| 1029 | 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | ||
| 1097 | 1097 | style_set_current_feature("setup"); |
| 1098 | 1098 | style_header("WWW Configuration"); |
| 1099 | 1099 | db_begin_transaction(); |
| 1100 | 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | 1101 | login_insert_csrf_secret(); |
| 1102 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1103 | - @ <hr /> | |
| 1102 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1103 | + @ <hr> | |
| 1104 | 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | 1107 | @ (Property: "project-name") |
| 1108 | 1108 | @ </p> |
| 1109 | - @ <hr /> | |
| 1109 | + @ <hr> | |
| 1110 | 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | 1111 | "project-description", "pd", "", 0); |
| 1112 | 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | 1113 | @ engines as well as a short RSS description. |
| 1114 | 1114 | @ (Property: "project-description")</p> |
| 1115 | - @ <hr /> | |
| 1115 | + @ <hr> | |
| 1116 | 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | 1117 | "eurl", "", 0); |
| 1118 | 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | ||
| 1130 | 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | 1131 | @ characters such as "/" and "\". |
| 1132 | 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | 1133 | @ (Property: "short-project-name") |
| 1134 | 1134 | @ </p> |
| 1135 | - @ <hr /> | |
| 1135 | + @ <hr> | |
| 1136 | 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | 1138 | @ a convenient place for newbies |
| 1139 | 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | 1142 | @ (Property: "download-tag") |
| 1143 | 1143 | @ </p> |
| 1144 | - @ <hr /> | |
| 1144 | + @ <hr> | |
| 1145 | 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | 1147 | @ option is selected and when no pathname is |
| 1148 | 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | 1149 | @ |
| @@ -1220,12 +1220,12 @@ | ||
| 1220 | 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | 1221 | @ (Property: sitemap-extra) |
| 1222 | 1222 | @ <p> |
| 1223 | 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | - @ <hr /> | |
| 1226 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1225 | + @ <hr> | |
| 1226 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1227 | 1227 | @ </div></form> |
| 1228 | 1228 | db_end_transaction(0); |
| 1229 | 1229 | style_finish_page(); |
| 1230 | 1230 | } |
| 1231 | 1231 | |
| @@ -1244,12 +1244,12 @@ | ||
| 1244 | 1244 | style_set_current_feature("setup"); |
| 1245 | 1245 | style_header("Wiki Configuration"); |
| 1246 | 1246 | db_begin_transaction(); |
| 1247 | 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | 1248 | login_insert_csrf_secret(); |
| 1249 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1250 | - @ <hr /> | |
| 1249 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1250 | + @ <hr> | |
| 1251 | 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | 1253 | @ <p> |
| 1254 | 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | ||
| 1259 | 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | 1262 | @ </ul> |
| 1263 | 1263 | @ (Property: "wiki-about")</p> |
| 1264 | - @ <hr /> | |
| 1264 | + @ <hr> | |
| 1265 | 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | ||
| 1279 | 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | 1282 | @ to generate unsafe HTML. |
| 1283 | 1283 | @ (Property: "safe-html")</p> |
| 1284 | - @ <hr /> | |
| 1284 | + @ <hr> | |
| 1285 | 1285 | @ The current interwiki tag map is as follows: |
| 1286 | 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | 1288 | @ modify the interwiki tag map. |
| 1289 | - @ <hr /> | |
| 1289 | + @ <hr> | |
| 1290 | 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | ||
| 1297 | 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | 1300 | @ editable wiki.</p> |
| 1301 | 1301 | @ (Property: "wiki-use-html") |
| 1302 | - @ <hr /> | |
| 1303 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1302 | + @ <hr> | |
| 1303 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1304 | 1304 | @ </div></form> |
| 1305 | 1305 | db_end_transaction(0); |
| 1306 | 1306 | style_finish_page(); |
| 1307 | 1307 | } |
| 1308 | 1308 | |
| @@ -1328,32 +1328,32 @@ | ||
| 1328 | 1328 | style_set_current_feature("setup"); |
| 1329 | 1329 | style_header("Chat Configuration"); |
| 1330 | 1330 | db_begin_transaction(); |
| 1331 | 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | 1332 | login_insert_csrf_secret(); |
| 1333 | - @ <input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1334 | - @ <hr /> | |
| 1333 | + @ <input type="submit" name="submit" value="Apply Changes"></p> | |
| 1334 | + @ <hr> | |
| 1335 | 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | 1338 | @ messages. |
| 1339 | 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | - @ <hr /> | |
| 1340 | + @ <hr> | |
| 1341 | 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | - @ <hr /> | |
| 1346 | + @ <hr> | |
| 1347 | 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | 1352 | @ to 0.5. |
| 1353 | 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | - @ <hr /> | |
| 1354 | + @ <hr> | |
| 1355 | 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | ||
| 1362 | 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | 1365 | @ should be larger rather than smaller. |
| 1366 | 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | - @ <hr /> | |
| 1367 | + @ <hr> | |
| 1368 | 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | 1373 | @ Suggested username: "chat-robot". |
| 1374 | 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | - @ <hr /> | |
| 1375 | + @ <hr> | |
| 1376 | 1376 | |
| 1377 | 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | 1381 | @ chat message has arrived. |
| 1382 | 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | 1383 | @ <hr/> |
| 1384 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1384 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1385 | 1385 | @ </div></form> |
| 1386 | 1386 | db_end_transaction(0); |
| 1387 | 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | 1388 | @ (function(){ |
| 1389 | 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | ||
| 1412 | 1412 | style_set_current_feature("setup"); |
| 1413 | 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | 1414 | db_begin_transaction(); |
| 1415 | 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | 1416 | login_insert_csrf_secret(); |
| 1417 | - @ <hr /> | |
| 1417 | + @ <hr> | |
| 1418 | 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | ||
| 1423 | 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | 1427 | @ |
| 1428 | - @ <hr /> | |
| 1428 | + @ <hr> | |
| 1429 | 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | ||
| 1435 | 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | 1438 | @ </p> |
| 1439 | 1439 | |
| 1440 | - @ <hr /> | |
| 1441 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 1440 | + @ <hr> | |
| 1441 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 1442 | 1442 | @ </div></form> |
| 1443 | 1443 | db_end_transaction(0); |
| 1444 | 1444 | style_finish_page(); |
| 1445 | 1445 | |
| 1446 | 1446 | } |
| @@ -1469,29 +1469,29 @@ | ||
| 1469 | 1469 | |
| 1470 | 1470 | style_set_current_feature("setup"); |
| 1471 | 1471 | style_header("Edit Ad Unit"); |
| 1472 | 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | 1473 | login_insert_csrf_secret(); |
| 1474 | - @ <b>Banner Ad-Unit:</b><br /> | |
| 1474 | + @ <b>Banner Ad-Unit:</b><br> | |
| 1475 | 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | - @ <br /> | |
| 1477 | - @ <b>Right-Column Ad-Unit:</b><br /> | |
| 1476 | + @ <br> | |
| 1477 | + @ <b>Right-Column Ad-Unit:</b><br> | |
| 1478 | 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | - @ <br /> | |
| 1479 | + @ <br> | |
| 1480 | 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | - @ <br /> | |
| 1482 | + @ <br> | |
| 1483 | 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | - @ <br /> | |
| 1485 | + @ <br> | |
| 1486 | 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | - @ <br /> | |
| 1489 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 1490 | - @ <input type="submit" name="clear" value="Delete Ad-Unit" /> | |
| 1488 | + @ <br> | |
| 1489 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 1490 | + @ <input type="submit" name="clear" value="Delete Ad-Unit"> | |
| 1491 | 1491 | @ </div></form> |
| 1492 | - @ <hr /> | |
| 1492 | + @ <hr> | |
| 1493 | 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | ||
| 1651 | 1651 | } |
| 1652 | 1652 | style_set_current_feature("setup"); |
| 1653 | 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | 1655 | @ and looks like this:</p> |
| 1656 | - @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \ | |
| 1657 | - @ alt="logo" border="1" /> | |
| 1656 | + @ <blockquote><p> | |
| 1657 | + @ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1"> | |
| 1658 | 1658 | @ </p></blockquote> |
| 1659 | 1659 | @ |
| 1660 | 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | ||
| 1665 | 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | 1668 | login_insert_csrf_secret(); |
| 1669 | 1669 | @ Logo Image file: |
| 1670 | - @ <input type="file" name="logoim" size="60" accept="image/*" /> | |
| 1670 | + @ <input type="file" name="logoim" size="60" accept="image/*"> | |
| 1671 | 1671 | @ <p align="center"> |
| 1672 | - @ <input type="submit" name="setlogo" value="Change Logo" /> | |
| 1673 | - @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> | |
| 1672 | + @ <input type="submit" name="setlogo" value="Change Logo"> | |
| 1673 | + @ <input type="submit" name="clrlogo" value="Revert To Default"></p> | |
| 1674 | 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | 1675 | @ </div></form> |
| 1676 | - @ <hr /> | |
| 1676 | + @ <hr> | |
| 1677 | 1677 | @ |
| 1678 | 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | 1679 | @ and looks like this:</p> |
| 1680 | 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | - @ alt="background" border=1 /> | |
| 1681 | + @ alt="background" border=1> | |
| 1682 | 1682 | @ </p></blockquote> |
| 1683 | 1683 | @ |
| 1684 | 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | ||
| 1689 | 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | 1691 | @ To change the background image, use the following form:</p> |
| 1692 | 1692 | login_insert_csrf_secret(); |
| 1693 | 1693 | @ Background image file: |
| 1694 | - @ <input type="file" name="bgim" size="60" accept="image/*" /> | |
| 1694 | + @ <input type="file" name="bgim" size="60" accept="image/*"> | |
| 1695 | 1695 | @ <p align="center"> |
| 1696 | - @ <input type="submit" name="setbg" value="Change Background" /> | |
| 1697 | - @ <input type="submit" name="clrbg" value="Revert To Default" /></p> | |
| 1696 | + @ <input type="submit" name="setbg" value="Change Background"> | |
| 1697 | + @ <input type="submit" name="clrbg" value="Revert To Default"></p> | |
| 1698 | 1698 | @ </div></form> |
| 1699 | 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | - @ <hr /> | |
| 1700 | + @ <hr> | |
| 1701 | 1701 | @ |
| 1702 | 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | 1703 | @ and looks like this:</p> |
| 1704 | 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | - @ alt="icon" border=1 /> | |
| 1705 | + @ alt="icon" border=1> | |
| 1706 | 1706 | @ </p></blockquote> |
| 1707 | 1707 | @ |
| 1708 | 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | ||
| 1713 | 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | 1714 | @ supports for icon images. |
| 1715 | 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | 1716 | login_insert_csrf_secret(); |
| 1717 | 1717 | @ Icon image file: |
| 1718 | - @ <input type="file" name="iconim" size="60" accept="image/*" /> | |
| 1718 | + @ <input type="file" name="iconim" size="60" accept="image/*"> | |
| 1719 | 1719 | @ <p align="center"> |
| 1720 | - @ <input type="submit" name="seticon" value="Change Icon" /> | |
| 1721 | - @ <input type="submit" name="clricon" value="Revert To Default" /></p> | |
| 1720 | + @ <input type="submit" name="seticon" value="Change Icon"> | |
| 1721 | + @ <input type="submit" name="clricon" value="Revert To Default"></p> | |
| 1722 | 1722 | @ </div></form> |
| 1723 | 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | - @ <hr /> | |
| 1724 | + @ <hr> | |
| 1725 | 1725 | @ |
| 1726 | 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | 1728 | @ take effect. </p> |
| 1729 | 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | ||
| 1804 | 1804 | go = 1; |
| 1805 | 1805 | } |
| 1806 | 1806 | @ |
| 1807 | 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | 1808 | login_insert_csrf_secret(); |
| 1809 | - @ SQL:<br /> | |
| 1810 | - @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br /> | |
| 1809 | + @ SQL:<br> | |
| 1810 | + @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br> | |
| 1811 | 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | ||
| 1827 | 1827 | int rc; |
| 1828 | 1828 | const char *zTail; |
| 1829 | 1829 | int nCol; |
| 1830 | 1830 | int nRow = 0; |
| 1831 | 1831 | int i; |
| 1832 | - @ <hr /> | |
| 1832 | + @ <hr> | |
| 1833 | 1833 | login_verify_csrf_secret(); |
| 1834 | 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | 1835 | search_sql_setup(g.db); |
| 1836 | 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | ||
| 1913 | 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | 1915 | @ |
| 1916 | 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | 1917 | login_insert_csrf_secret(); |
| 1918 | - @ TH1:<br /> | |
| 1919 | - @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br /> | |
| 1918 | + @ TH1:<br> | |
| 1919 | + @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> | |
| 1920 | 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | 1921 | @ </form> |
| 1922 | 1922 | if( go ){ |
| 1923 | 1923 | const char *zR; |
| 1924 | 1924 | int rc; |
| 1925 | 1925 | int n; |
| 1926 | - @ <hr /> | |
| 1926 | + @ <hr> | |
| 1927 | 1927 | login_verify_csrf_secret(); |
| 1928 | 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | 1930 | if( rc==TH_OK ){ |
| 1931 | 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | ||
| 2005 | 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | 2006 | } |
| 2007 | 2007 | style_finish_page(); |
| 2008 | 2008 | } |
| 2009 | 2009 | |
| 2010 | + | |
| 2011 | +/* | |
| 2012 | +** Renders a selection list of values for the search-tokenizer | |
| 2013 | +** setting, using the form field name "ftstok". | |
| 2014 | +*/ | |
| 2015 | +static void select_fts_tokenizer(void){ | |
| 2016 | + const char *const aTokenizer[] = { | |
| 2017 | + "off", "None", | |
| 2018 | + "porter", "Porter Stemmer", | |
| 2019 | + "trigram", "Trigram" | |
| 2020 | + }; | |
| 2021 | + multiple_choice_attribute("FTS Tokenizer", "search-tokenizer", | |
| 2022 | + "ftstok", "off", 3, aTokenizer); | |
| 2023 | +} | |
| 2024 | + | |
| 2010 | 2025 | /* |
| 2011 | 2026 | ** WEBPAGE: srchsetup |
| 2012 | 2027 | ** |
| 2013 | 2028 | ** Configure the search engine. Requires Admin privilege. |
| 2014 | 2029 | */ |
| @@ -2024,11 +2039,11 @@ | ||
| 2024 | 2039 | login_insert_csrf_secret(); |
| 2025 | 2040 | @ <div style="text-align:center;font-weight:bold;"> |
| 2026 | 2041 | @ Server-specific settings that affect the |
| 2027 | 2042 | @ <a href="%R/search">/search</a> webpage. |
| 2028 | 2043 | @ </div> |
| 2029 | - @ <hr /> | |
| 2044 | + @ <hr> | |
| 2030 | 2045 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2031 | 2046 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2032 | 2047 | @ of GLOB expressions that identify all documents within the source |
| 2033 | 2048 | @ tree that are to be searched when "Document Search" is enabled. |
| 2034 | 2049 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | ||
| 2040 | 2055 | @ files.</tr> |
| 2041 | 2056 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2042 | 2057 | @ <tr><td><i>(blank)</i><td> |
| 2043 | 2058 | @ <td>Search nothing. (Disables document search).</tr> |
| 2044 | 2059 | @ </table> |
| 2045 | - @ <hr /> | |
| 2060 | + @ <hr> | |
| 2046 | 2061 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2047 | 2062 | @ <p>When searching documents, use the versions of the files found at the |
| 2048 | 2063 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2049 | 2064 | @ Document search is disabled if blank. |
| 2050 | - @ <hr /> | |
| 2065 | + @ <hr> | |
| 2051 | 2066 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2052 | - @ <br /> | |
| 2067 | + @ <br> | |
| 2053 | 2068 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2054 | - @ <br /> | |
| 2069 | + @ <br> | |
| 2055 | 2070 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2056 | - @ <br /> | |
| 2071 | + @ <br> | |
| 2057 | 2072 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2058 | - @ <br /> | |
| 2073 | + @ <br> | |
| 2059 | 2074 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2060 | - @ <br /> | |
| 2075 | + @ <br> | |
| 2061 | 2076 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2062 | - @ <hr /> | |
| 2063 | - @ <p><input type="submit" name="submit" value="Apply Changes" /></p> | |
| 2064 | - @ <hr /> | |
| 2077 | + @ <hr> | |
| 2078 | + @ <p><input type="submit" name="submit" value="Apply Changes"></p> | |
| 2079 | + @ <hr> | |
| 2065 | 2080 | if( P("fts0") ){ |
| 2066 | 2081 | search_drop_index(); |
| 2067 | 2082 | }else if( P("fts1") ){ |
| 2083 | + const char *zTokenizer = PD("ftstok","off"); | |
| 2084 | + search_set_tokenizer(zTokenizer); | |
| 2068 | 2085 | search_drop_index(); |
| 2069 | 2086 | search_create_index(); |
| 2070 | 2087 | search_fill_index(); |
| 2071 | 2088 | search_update_index(search_restrict(SRCH_ALL)); |
| 2072 | 2089 | } |
| 2073 | 2090 | if( search_index_exists() ){ |
| 2074 | 2091 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2075 | 2092 | @ The index helps search run faster, especially on large repositories, |
| 2076 | 2093 | @ but takes up space.</p> |
| 2077 | - onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); | |
| 2094 | + select_fts_tokenizer(); | |
| 2078 | 2095 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2079 | 2096 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2080 | 2097 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2081 | 2098 | }else{ |
| 2082 | 2099 | @ <p>The SQLite search index is disabled. All searching will be |
| 2083 | 2100 | @ a full-text scan. This usually works fine, but can be slow for |
| 2084 | 2101 | @ larger repositories.</p> |
| 2085 | - onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); | |
| 2102 | + select_fts_tokenizer(); | |
| 2086 | 2103 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2087 | 2104 | } |
| 2088 | 2105 | @ </div></form> |
| 2089 | 2106 | style_finish_page(); |
| 2090 | 2107 | } |
| 2091 | 2108 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -219,11 +219,11 @@ | |
| 219 | @ checked="checked" \ |
| 220 | } |
| 221 | if( disabled ){ |
| 222 | @ disabled="disabled" \ |
| 223 | } |
| 224 | @ /> <b>%s(zLabel)</b></label> |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | ** Generate an entry box for an attribute. |
| 229 | */ |
| @@ -251,11 +251,11 @@ | |
| 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | if( disabled ){ |
| 254 | @ disabled="disabled" \ |
| 255 | } |
| 256 | @ /> <b>%s(zLabel)</b> |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Generate a text box for an attribute. |
| 261 | */ |
| @@ -371,11 +371,11 @@ | |
| 371 | @ |
| 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | @ <blockquote> |
| 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | @ <br /> |
| 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | @ </blockquote> |
| 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | |
| 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | @ defend the site against robots. |
| 414 | @ |
| 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | login_insert_csrf_secret(); |
| 417 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 418 | @ <hr /> |
| 419 | addAutoHyperlinkSettings(); |
| 420 | @ <hr /> |
| 421 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 422 | @ </div></form> |
| 423 | db_end_transaction(0); |
| 424 | style_finish_page(); |
| 425 | } |
| 426 | |
| @@ -444,12 +444,12 @@ | |
| 444 | style_set_current_feature("setup"); |
| 445 | style_header("Access Control Settings"); |
| 446 | db_begin_transaction(); |
| 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | login_insert_csrf_secret(); |
| 449 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 450 | @ <hr /> |
| 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | |
| 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | @ "2" otherwise.) |
| 462 | @ <hr /> |
| 463 | onoff_attribute("Require password for local access", |
| 464 | "localauth", "localauth", 0, 0); |
| 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | |
| 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | @ in the CGI script. |
| 485 | @ </ol> |
| 486 | @ (Property: "localauth") |
| 487 | @ |
| 488 | @ <hr /> |
| 489 | onoff_attribute("Enable /test_env", |
| 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | @ the /test_env page. |
| 494 | @ (Property: "test_env_enable") |
| 495 | @ </p> |
| 496 | @ |
| 497 | @ <hr /> |
| 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | @ access the /artifact_stats page. |
| 503 | @ (Property: "artifact_stats_enable") |
| 504 | @ </p> |
| 505 | @ |
| 506 | @ <hr /> |
| 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | @ (Property: "remote_user_ok") |
| 513 | @ </p> |
| 514 | @ |
| 515 | @ <hr /> |
| 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | @ (Property: "http_authentication_ok") |
| 522 | @ </p> |
| 523 | @ |
| 524 | @ <hr /> |
| 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | "8766", 0); |
| 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | @ to a year. |
| 530 | @ (Property: "cookie-expire")</p> |
| 531 | |
| 532 | @ <hr /> |
| 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | "5000000", 0); |
| 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | |
| 541 | @ <hr /> |
| 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | "30", 0); |
| 544 | |
| 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | |
| 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | @ (Property: "max-download-time")</p> |
| 550 | |
| 551 | @ <a id="slal"></a> |
| 552 | @ <hr /> |
| 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | "0.0", 0); |
| 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | |
| 562 | @ (Property: "max-loadavg")</p> |
| 563 | |
| 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | ** are also accessible from the /setup_robot page. |
| 566 | */ |
| 567 | @ <hr /> |
| 568 | addAutoHyperlinkSettings(); |
| 569 | |
| 570 | @ <hr /> |
| 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | |
| 577 | @ <hr /> |
| 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | "pubpage", "", 0); |
| 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | @ without needing a login and using the privileges given by the |
| 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | |
| 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | @ allowing them to see the rest of the source code. |
| 589 | @ (Property: "public-pages") |
| 590 | @ </p> |
| 591 | |
| 592 | @ <hr /> |
| 593 | onoff_attribute("Allow users to register themselves", |
| 594 | "self-register", "selfreg", 0, 0); |
| 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | @ enabled. |
| 599 | @ (Property: "self-register")</p> |
| 600 | |
| 601 | @ <hr /> |
| 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | @ password reset page be sent to their email address of record. This |
| 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | @ administrator intervention. |
| 608 | @ (Property: "self-pw-reset")</p> |
| 609 | |
| 610 | @ <hr /> |
| 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | @ added until the email address associated with the self-registration |
| 616 | @ has been verified. This setting only makes sense if |
| 617 | @ email notifications are enabled. |
| 618 | @ (Property: "selfreg-verify")</p> |
| 619 | |
| 620 | @ <hr /> |
| 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | @ page, they are redirected to /register or /login. |
| 626 | @ (Property: "anon-subscribe")</p> |
| 627 | |
| 628 | @ <hr /> |
| 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | @ organization or group based on their email address. |
| 636 | @ (Property: "auth-sub-email")</p> |
| 637 | |
| 638 | @ <hr /> |
| 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | "defaultperms", "u", 0); |
| 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | |
| 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | @ (Property: "default-perms") |
| 648 | @ </p> |
| 649 | |
| 650 | @ <hr /> |
| 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | |
| 659 | @ <hr /> |
| 660 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 661 | @ </div></form> |
| 662 | db_end_transaction(0); |
| 663 | style_finish_page(); |
| 664 | } |
| 665 | |
| @@ -762,11 +762,11 @@ | |
| 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | login_insert_csrf_secret(); |
| 764 | @ To leave this login group press |
| 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | @ </form></p> |
| 767 | @ <hr /><h2>Implementation Details</h2> |
| 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | @ data-init-sort='1'> |
| 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | |
| 814 | style_set_current_feature("setup"); |
| 815 | style_header("Timeline Display Preferences"); |
| 816 | db_begin_transaction(); |
| 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | login_insert_csrf_secret(); |
| 819 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 820 | |
| 821 | @ <hr /> |
| 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | |
| 828 | @ <hr /> |
| 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | |
| 836 | @ <hr /> |
| 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | |
| 843 | @ <hr /> |
| 844 | onoff_attribute("Break comments at newline characters", |
| 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | @ a line break on the display. |
| 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | |
| 850 | @ <hr /> |
| 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | "timeline-utc", "utc", 1, 0); |
| 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | |
| 866 | }else{ |
| 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | } |
| 869 | @ <p>(Property: "timeline-utc") |
| 870 | |
| 871 | @ <hr /> |
| 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | |
| 877 | @ <hr /> |
| 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | "tldl", "50", 0); |
| 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | |
| 884 | @ <hr /> |
| 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | |
| 893 | @ <hr /> |
| 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | @ "0" there is no length limit. |
| 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | |
| 900 | @ <hr /> |
| 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | @ <br> |
| 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | |
| 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | |
| 916 | @ <hr /> |
| 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | |
| 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | @ right of the entry. |
| 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | |
| 932 | @ <hr /> |
| 933 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 934 | @ </div></form> |
| 935 | db_end_transaction(0); |
| 936 | style_finish_page(); |
| 937 | } |
| 938 | |
| @@ -964,11 +964,11 @@ | |
| 964 | db_begin_transaction(); |
| 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | @ by the contents of managed files named |
| 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | @ changed on this screen.</p><hr /><p> |
| 970 | @ |
| 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | login_insert_csrf_secret(); |
| 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | |
| 978 | onoff_attribute("", pSet->name, |
| 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | if( pSet->versionable ){ |
| 983 | @ (v)<br /> |
| 984 | } else { |
| 985 | @ <br /> |
| 986 | } |
| 987 | } |
| 988 | } |
| 989 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | @ <table> |
| 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | |
| 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | if( pSet->versionable ){ |
| 1017 | @ (v)<br /> |
| 1018 | } else { |
| 1019 | @ <br /> |
| 1020 | } |
| 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | @<br /> |
| 1025 | } |
| 1026 | } |
| 1027 | @ </td></tr></table> |
| 1028 | @ </div></form> |
| 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | |
| 1097 | style_set_current_feature("setup"); |
| 1098 | style_header("WWW Configuration"); |
| 1099 | db_begin_transaction(); |
| 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | login_insert_csrf_secret(); |
| 1102 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1103 | @ <hr /> |
| 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | @ (Property: "project-name") |
| 1108 | @ </p> |
| 1109 | @ <hr /> |
| 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | "project-description", "pd", "", 0); |
| 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | @ engines as well as a short RSS description. |
| 1114 | @ (Property: "project-description")</p> |
| 1115 | @ <hr /> |
| 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | "eurl", "", 0); |
| 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | |
| 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | @ characters such as "/" and "\". |
| 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | @ (Property: "short-project-name") |
| 1134 | @ </p> |
| 1135 | @ <hr /> |
| 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | @ a convenient place for newbies |
| 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | @ (Property: "download-tag") |
| 1143 | @ </p> |
| 1144 | @ <hr /> |
| 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | @ option is selected and when no pathname is |
| 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | @ |
| @@ -1220,12 +1220,12 @@ | |
| 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | @ (Property: sitemap-extra) |
| 1222 | @ <p> |
| 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | @ <hr /> |
| 1226 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1227 | @ </div></form> |
| 1228 | db_end_transaction(0); |
| 1229 | style_finish_page(); |
| 1230 | } |
| 1231 | |
| @@ -1244,12 +1244,12 @@ | |
| 1244 | style_set_current_feature("setup"); |
| 1245 | style_header("Wiki Configuration"); |
| 1246 | db_begin_transaction(); |
| 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | login_insert_csrf_secret(); |
| 1249 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1250 | @ <hr /> |
| 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | @ <p> |
| 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | |
| 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | @ </ul> |
| 1263 | @ (Property: "wiki-about")</p> |
| 1264 | @ <hr /> |
| 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | |
| 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | @ to generate unsafe HTML. |
| 1283 | @ (Property: "safe-html")</p> |
| 1284 | @ <hr /> |
| 1285 | @ The current interwiki tag map is as follows: |
| 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | @ modify the interwiki tag map. |
| 1289 | @ <hr /> |
| 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | |
| 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | @ editable wiki.</p> |
| 1301 | @ (Property: "wiki-use-html") |
| 1302 | @ <hr /> |
| 1303 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1304 | @ </div></form> |
| 1305 | db_end_transaction(0); |
| 1306 | style_finish_page(); |
| 1307 | } |
| 1308 | |
| @@ -1328,32 +1328,32 @@ | |
| 1328 | style_set_current_feature("setup"); |
| 1329 | style_header("Chat Configuration"); |
| 1330 | db_begin_transaction(); |
| 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | login_insert_csrf_secret(); |
| 1333 | @ <input type="submit" name="submit" value="Apply Changes" /></p> |
| 1334 | @ <hr /> |
| 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | @ messages. |
| 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | @ <hr /> |
| 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | @ <hr /> |
| 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | @ to 0.5. |
| 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | @ <hr /> |
| 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | |
| 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | @ should be larger rather than smaller. |
| 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | @ <hr /> |
| 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | @ Suggested username: "chat-robot". |
| 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | @ <hr /> |
| 1376 | |
| 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | @ chat message has arrived. |
| 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | @ <hr/> |
| 1384 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1385 | @ </div></form> |
| 1386 | db_end_transaction(0); |
| 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | @ (function(){ |
| 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | style_set_current_feature("setup"); |
| 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | db_begin_transaction(); |
| 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | login_insert_csrf_secret(); |
| 1417 | @ <hr /> |
| 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | |
| 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | @ |
| 1428 | @ <hr /> |
| 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | |
| 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | @ </p> |
| 1439 | |
| 1440 | @ <hr /> |
| 1441 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 1442 | @ </div></form> |
| 1443 | db_end_transaction(0); |
| 1444 | style_finish_page(); |
| 1445 | |
| 1446 | } |
| @@ -1469,29 +1469,29 @@ | |
| 1469 | |
| 1470 | style_set_current_feature("setup"); |
| 1471 | style_header("Edit Ad Unit"); |
| 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | login_insert_csrf_secret(); |
| 1474 | @ <b>Banner Ad-Unit:</b><br /> |
| 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | @ <br /> |
| 1477 | @ <b>Right-Column Ad-Unit:</b><br /> |
| 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | @ <br /> |
| 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | @ <br /> |
| 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | @ <br /> |
| 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | @ <br /> |
| 1489 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1490 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> |
| 1491 | @ </div></form> |
| 1492 | @ <hr /> |
| 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | |
| 1651 | } |
| 1652 | style_set_current_feature("setup"); |
| 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | @ and looks like this:</p> |
| 1656 | @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \ |
| 1657 | @ alt="logo" border="1" /> |
| 1658 | @ </p></blockquote> |
| 1659 | @ |
| 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | |
| 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | login_insert_csrf_secret(); |
| 1669 | @ Logo Image file: |
| 1670 | @ <input type="file" name="logoim" size="60" accept="image/*" /> |
| 1671 | @ <p align="center"> |
| 1672 | @ <input type="submit" name="setlogo" value="Change Logo" /> |
| 1673 | @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> |
| 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | @ </div></form> |
| 1676 | @ <hr /> |
| 1677 | @ |
| 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | @ and looks like this:</p> |
| 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | @ alt="background" border=1 /> |
| 1682 | @ </p></blockquote> |
| 1683 | @ |
| 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | |
| 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | @ To change the background image, use the following form:</p> |
| 1692 | login_insert_csrf_secret(); |
| 1693 | @ Background image file: |
| 1694 | @ <input type="file" name="bgim" size="60" accept="image/*" /> |
| 1695 | @ <p align="center"> |
| 1696 | @ <input type="submit" name="setbg" value="Change Background" /> |
| 1697 | @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
| 1698 | @ </div></form> |
| 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | @ <hr /> |
| 1701 | @ |
| 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | @ and looks like this:</p> |
| 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | @ alt="icon" border=1 /> |
| 1706 | @ </p></blockquote> |
| 1707 | @ |
| 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | |
| 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | @ supports for icon images. |
| 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | login_insert_csrf_secret(); |
| 1717 | @ Icon image file: |
| 1718 | @ <input type="file" name="iconim" size="60" accept="image/*" /> |
| 1719 | @ <p align="center"> |
| 1720 | @ <input type="submit" name="seticon" value="Change Icon" /> |
| 1721 | @ <input type="submit" name="clricon" value="Revert To Default" /></p> |
| 1722 | @ </div></form> |
| 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | @ <hr /> |
| 1725 | @ |
| 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | @ take effect. </p> |
| 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | |
| 1804 | go = 1; |
| 1805 | } |
| 1806 | @ |
| 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | login_insert_csrf_secret(); |
| 1809 | @ SQL:<br /> |
| 1810 | @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br /> |
| 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | |
| 1827 | int rc; |
| 1828 | const char *zTail; |
| 1829 | int nCol; |
| 1830 | int nRow = 0; |
| 1831 | int i; |
| 1832 | @ <hr /> |
| 1833 | login_verify_csrf_secret(); |
| 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | search_sql_setup(g.db); |
| 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | |
| 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | @ |
| 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | login_insert_csrf_secret(); |
| 1918 | @ TH1:<br /> |
| 1919 | @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br /> |
| 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | @ </form> |
| 1922 | if( go ){ |
| 1923 | const char *zR; |
| 1924 | int rc; |
| 1925 | int n; |
| 1926 | @ <hr /> |
| 1927 | login_verify_csrf_secret(); |
| 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | if( rc==TH_OK ){ |
| 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | |
| 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | } |
| 2007 | style_finish_page(); |
| 2008 | } |
| 2009 | |
| 2010 | /* |
| 2011 | ** WEBPAGE: srchsetup |
| 2012 | ** |
| 2013 | ** Configure the search engine. Requires Admin privilege. |
| 2014 | */ |
| @@ -2024,11 +2039,11 @@ | |
| 2024 | login_insert_csrf_secret(); |
| 2025 | @ <div style="text-align:center;font-weight:bold;"> |
| 2026 | @ Server-specific settings that affect the |
| 2027 | @ <a href="%R/search">/search</a> webpage. |
| 2028 | @ </div> |
| 2029 | @ <hr /> |
| 2030 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2031 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2032 | @ of GLOB expressions that identify all documents within the source |
| 2033 | @ tree that are to be searched when "Document Search" is enabled. |
| 2034 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | |
| 2040 | @ files.</tr> |
| 2041 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2042 | @ <tr><td><i>(blank)</i><td> |
| 2043 | @ <td>Search nothing. (Disables document search).</tr> |
| 2044 | @ </table> |
| 2045 | @ <hr /> |
| 2046 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2047 | @ <p>When searching documents, use the versions of the files found at the |
| 2048 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2049 | @ Document search is disabled if blank. |
| 2050 | @ <hr /> |
| 2051 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2052 | @ <br /> |
| 2053 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2054 | @ <br /> |
| 2055 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2056 | @ <br /> |
| 2057 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2058 | @ <br /> |
| 2059 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2060 | @ <br /> |
| 2061 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2062 | @ <hr /> |
| 2063 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 2064 | @ <hr /> |
| 2065 | if( P("fts0") ){ |
| 2066 | search_drop_index(); |
| 2067 | }else if( P("fts1") ){ |
| 2068 | search_drop_index(); |
| 2069 | search_create_index(); |
| 2070 | search_fill_index(); |
| 2071 | search_update_index(search_restrict(SRCH_ALL)); |
| 2072 | } |
| 2073 | if( search_index_exists() ){ |
| 2074 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2075 | @ The index helps search run faster, especially on large repositories, |
| 2076 | @ but takes up space.</p> |
| 2077 | onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); |
| 2078 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2079 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2080 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2081 | }else{ |
| 2082 | @ <p>The SQLite search index is disabled. All searching will be |
| 2083 | @ a full-text scan. This usually works fine, but can be slow for |
| 2084 | @ larger repositories.</p> |
| 2085 | onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); |
| 2086 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2087 | } |
| 2088 | @ </div></form> |
| 2089 | style_finish_page(); |
| 2090 | } |
| 2091 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -219,11 +219,11 @@ | |
| 219 | @ checked="checked" \ |
| 220 | } |
| 221 | if( disabled ){ |
| 222 | @ disabled="disabled" \ |
| 223 | } |
| 224 | @ > <b>%s(zLabel)</b></label> |
| 225 | } |
| 226 | |
| 227 | /* |
| 228 | ** Generate an entry box for an attribute. |
| 229 | */ |
| @@ -251,11 +251,11 @@ | |
| 251 | @ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \ |
| 252 | @ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \ |
| 253 | if( disabled ){ |
| 254 | @ disabled="disabled" \ |
| 255 | } |
| 256 | @ > <b>%s(zLabel)</b> |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Generate a text box for an attribute. |
| 261 | */ |
| @@ -371,11 +371,11 @@ | |
| 371 | @ |
| 372 | @ <p>Additional parameters that control the behavior of Javascript:</p> |
| 373 | @ <blockquote> |
| 374 | entry_attribute("Delay in milliseconds before enabling hyperlinks", 5, |
| 375 | "auto-hyperlink-delay", "ah-delay", "50", 0); |
| 376 | @ <br> |
| 377 | onoff_attribute("Also require a mouse event before enabling hyperlinks", |
| 378 | "auto-hyperlink-mouseover", "ahmo", 0, 0); |
| 379 | @ </blockquote> |
| 380 | @ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds |
| 381 | @ and "require a mouse event" should be turned on. These values only come |
| @@ -412,15 +412,15 @@ | |
| 412 | @ <p>The settings on this page are intended to help site administrators |
| 413 | @ defend the site against robots. |
| 414 | @ |
| 415 | @ <form action="%R/setup_robot" method="post"><div> |
| 416 | login_insert_csrf_secret(); |
| 417 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 418 | @ <hr> |
| 419 | addAutoHyperlinkSettings(); |
| 420 | @ <hr> |
| 421 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 422 | @ </div></form> |
| 423 | db_end_transaction(0); |
| 424 | style_finish_page(); |
| 425 | } |
| 426 | |
| @@ -444,12 +444,12 @@ | |
| 444 | style_set_current_feature("setup"); |
| 445 | style_header("Access Control Settings"); |
| 446 | db_begin_transaction(); |
| 447 | @ <form action="%R/setup_access" method="post"><div> |
| 448 | login_insert_csrf_secret(); |
| 449 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 450 | @ <hr> |
| 451 | multiple_choice_attribute("Redirect to HTTPS", |
| 452 | "redirect-to-https", "redirhttps", "0", |
| 453 | count(azRedirectOpts)/2, azRedirectOpts); |
| 454 | @ <p>Force the use of HTTPS by redirecting to HTTPS when an |
| 455 | @ unencrypted request is received. This feature can be enabled |
| @@ -457,11 +457,11 @@ | |
| 457 | @ <p>Further details: When enabled, this option causes the $secureurl TH1 |
| 458 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 459 | @ $secureurl is just an alias for $baseurl. |
| 460 | @ (Property: "redirect-to-https". "0" for off, "1" for Login page only, |
| 461 | @ "2" otherwise.) |
| 462 | @ <hr> |
| 463 | onoff_attribute("Require password for local access", |
| 464 | "localauth", "localauth", 0, 0); |
| 465 | @ <p>When enabled, the password sign-in is always required for |
| 466 | @ web access. When disabled, unrestricted web access from 127.0.0.1 |
| 467 | @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or |
| @@ -483,64 +483,64 @@ | |
| 483 | @ <li> The server is started from CGI without the "localauth" keyword |
| 484 | @ in the CGI script. |
| 485 | @ </ol> |
| 486 | @ (Property: "localauth") |
| 487 | @ |
| 488 | @ <hr> |
| 489 | onoff_attribute("Enable /test_env", |
| 490 | "test_env_enable", "test_env_enable", 0, 0); |
| 491 | @ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all |
| 492 | @ users. When disabled (the default) only users Admin and Setup can visit |
| 493 | @ the /test_env page. |
| 494 | @ (Property: "test_env_enable") |
| 495 | @ </p> |
| 496 | @ |
| 497 | @ <hr> |
| 498 | onoff_attribute("Enable /artifact_stats", |
| 499 | "artifact_stats_enable", "artifact_stats_enable", 0, 0); |
| 500 | @ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all |
| 501 | @ users. When disabled (the default) only users with check-in privilege may |
| 502 | @ access the /artifact_stats page. |
| 503 | @ (Property: "artifact_stats_enable") |
| 504 | @ </p> |
| 505 | @ |
| 506 | @ <hr> |
| 507 | onoff_attribute("Allow REMOTE_USER authentication", |
| 508 | "remote_user_ok", "remote_user_ok", 0, 0); |
| 509 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 510 | @ login name of a valid user and no other login credentials are available, |
| 511 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 512 | @ (Property: "remote_user_ok") |
| 513 | @ </p> |
| 514 | @ |
| 515 | @ <hr> |
| 516 | onoff_attribute("Allow HTTP_AUTHENTICATION authentication", |
| 517 | "http_authentication_ok", "http_authentication_ok", 0, 0); |
| 518 | @ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment |
| 519 | @ variable or the "Authentication:" HTTP header to find the username and |
| 520 | @ password. This is another way of supporting Basic Authentication. |
| 521 | @ (Property: "http_authentication_ok") |
| 522 | @ </p> |
| 523 | @ |
| 524 | @ <hr> |
| 525 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", |
| 526 | "8766", 0); |
| 527 | @ <p>The number of hours for which a login is valid. This must be a |
| 528 | @ positive number. The default is 8766 hours which is approximately equal |
| 529 | @ to a year. |
| 530 | @ (Property: "cookie-expire")</p> |
| 531 | |
| 532 | @ <hr> |
| 533 | entry_attribute("Download packet limit", 10, "max-download", "mxdwn", |
| 534 | "5000000", 0); |
| 535 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 536 | @ to this many bytes, uncompressed. If the client requires more data |
| 537 | @ than this, then the client will issue multiple HTTP requests. |
| 538 | @ Values below 1 million are not recommended. 5 million is a |
| 539 | @ reasonable number. (Property: "max-download")</p> |
| 540 | |
| 541 | @ <hr> |
| 542 | entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt", |
| 543 | "30", 0); |
| 544 | |
| 545 | @ <p>Fossil tries to spend less than this many seconds gathering |
| 546 | @ the out-bound data of sync, clone, and pull packets. |
| @@ -547,11 +547,11 @@ | |
| 547 | @ If the client request takes longer, a partial reply is given similar |
| 548 | @ to the download packet limit. 30s is a reasonable default. |
| 549 | @ (Property: "max-download-time")</p> |
| 550 | |
| 551 | @ <a id="slal"></a> |
| 552 | @ <hr> |
| 553 | entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", |
| 554 | "0.0", 0); |
| 555 | @ <p>Some expensive operations (such as computing tarballs, zip archives, |
| 556 | @ or annotation/blame pages) are prohibited if the load average on the host |
| 557 | @ computer is too large. Set the threshold for disallowing expensive |
| @@ -562,21 +562,21 @@ | |
| 562 | @ (Property: "max-loadavg")</p> |
| 563 | |
| 564 | /* Add the auto-hyperlink settings controls. These same controls |
| 565 | ** are also accessible from the /setup_robot page. |
| 566 | */ |
| 567 | @ <hr> |
| 568 | addAutoHyperlinkSettings(); |
| 569 | |
| 570 | @ <hr> |
| 571 | onoff_attribute("Require a CAPTCHA if not logged in", |
| 572 | "require-captcha", "reqcapt", 1, 0); |
| 573 | @ <p>Require a CAPTCHA for edit operations (appending, creating, or |
| 574 | @ editing wiki or tickets or adding attachments to wiki or tickets) |
| 575 | @ for users who are not logged in. (Property: "require-captcha")</p> |
| 576 | |
| 577 | @ <hr> |
| 578 | entry_attribute("Public pages", 30, "public-pages", |
| 579 | "pubpage", "", 0); |
| 580 | @ <p>A comma-separated list of glob patterns for pages that are accessible |
| 581 | @ without needing a login and using the privileges given by the |
| 582 | @ "Default privileges" setting below. |
| @@ -587,57 +587,57 @@ | |
| 587 | @ latest version of the embedded documentation in the www/ folder without |
| 588 | @ allowing them to see the rest of the source code. |
| 589 | @ (Property: "public-pages") |
| 590 | @ </p> |
| 591 | |
| 592 | @ <hr> |
| 593 | onoff_attribute("Allow users to register themselves", |
| 594 | "self-register", "selfreg", 0, 0); |
| 595 | @ <p>Allow users to register themselves on the /register webpage. |
| 596 | @ A self-registration creates a new entry in the USER table and |
| 597 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 598 | @ enabled. |
| 599 | @ (Property: "self-register")</p> |
| 600 | |
| 601 | @ <hr> |
| 602 | onoff_attribute("Allow users to reset their own passwords", |
| 603 | "self-pw-reset", "selfpw", 0, 0); |
| 604 | @ <p>Allow users to request that an email contains a hyperlink to a |
| 605 | @ password reset page be sent to their email address of record. This |
| 606 | @ enables forgetful users to recover their forgotten passwords without |
| 607 | @ administrator intervention. |
| 608 | @ (Property: "self-pw-reset")</p> |
| 609 | |
| 610 | @ <hr> |
| 611 | onoff_attribute("Email verification required for self-registration", |
| 612 | "selfreg-verify", "sfverify", 0, 0); |
| 613 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 614 | @ with only capabilities "7". The default user capabilities are not |
| 615 | @ added until the email address associated with the self-registration |
| 616 | @ has been verified. This setting only makes sense if |
| 617 | @ email notifications are enabled. |
| 618 | @ (Property: "selfreg-verify")</p> |
| 619 | |
| 620 | @ <hr> |
| 621 | onoff_attribute("Allow anonymous subscriptions", |
| 622 | "anon-subscribe", "anonsub", 1, 0); |
| 623 | @ <p>If disabled, email notification subscriptions are only allowed |
| 624 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 625 | @ page, they are redirected to /register or /login. |
| 626 | @ (Property: "anon-subscribe")</p> |
| 627 | |
| 628 | @ <hr> |
| 629 | entry_attribute("Authorized subscription email addresses", 35, |
| 630 | "auth-sub-email", "asemail", "", 0); |
| 631 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 632 | @ email addresses that are authorized to subscriptions. If blank |
| 633 | @ (the usual case), then any email address can be used to self-register. |
| 634 | @ This setting is used to limit subscriptions to members of a particular |
| 635 | @ organization or group based on their email address. |
| 636 | @ (Property: "auth-sub-email")</p> |
| 637 | |
| 638 | @ <hr> |
| 639 | entry_attribute("Default privileges", 10, "default-perms", |
| 640 | "defaultperms", "u", 0); |
| 641 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 642 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 643 | @ pages identified by the public-pages glob pattern above, or <li> |
| @@ -645,21 +645,21 @@ | |
| 645 | @ <p>Recommended value: "u" for Reader. |
| 646 | @ <a href="%R/setup_ucap_list">Capability Key</a>. |
| 647 | @ (Property: "default-perms") |
| 648 | @ </p> |
| 649 | |
| 650 | @ <hr> |
| 651 | onoff_attribute("Show javascript button to fill in CAPTCHA", |
| 652 | "auto-captcha", "autocaptcha", 0, 0); |
| 653 | @ <p>When enabled, a button appears on the login screen for user |
| 654 | @ "anonymous" that will automatically fill in the CAPTCHA password. |
| 655 | @ This is less secure than forcing the user to do it manually, but is |
| 656 | @ probably secure enough and it is certainly more convenient for |
| 657 | @ anonymous users. (Property: "auto-captcha")</p> |
| 658 | |
| 659 | @ <hr> |
| 660 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 661 | @ </div></form> |
| 662 | db_end_transaction(0); |
| 663 | style_finish_page(); |
| 664 | } |
| 665 | |
| @@ -762,11 +762,11 @@ | |
| 762 | @ <p><form action="%R/setup_login_group" method="post"><div> |
| 763 | login_insert_csrf_secret(); |
| 764 | @ To leave this login group press |
| 765 | @ <input type="submit" value="Leave Login Group" name="leave"> |
| 766 | @ </form></p> |
| 767 | @ <hr><h2>Implementation Details</h2> |
| 768 | @ <p>The following are fields from the CONFIG table related to login-groups, |
| 769 | @ provided here for instructional and debugging purposes:</p> |
| 770 | @ <table border='1' class='sortable' data-column-types='ttt' \ |
| 771 | @ data-init-sort='1'> |
| 772 | @ <thead><tr> |
| @@ -814,42 +814,42 @@ | |
| 814 | style_set_current_feature("setup"); |
| 815 | style_header("Timeline Display Preferences"); |
| 816 | db_begin_transaction(); |
| 817 | @ <form action="%R/setup_timeline" method="post"><div> |
| 818 | login_insert_csrf_secret(); |
| 819 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 820 | |
| 821 | @ <hr> |
| 822 | onoff_attribute("Allow block-markup in timeline", |
| 823 | "timeline-block-markup", "tbm", 0, 0); |
| 824 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 825 | @ without block markup such as paragraphs, tables, etc. |
| 826 | @ (Property: "timeline-block-markup")</p> |
| 827 | |
| 828 | @ <hr> |
| 829 | onoff_attribute("Plaintext comments on timelines", |
| 830 | "timeline-plaintext", "tpt", 0, 0); |
| 831 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 832 | @ without any wiki or HTML interpretation. Use CSS to change |
| 833 | @ display formatting features such as fonts and line-wrapping behavior. |
| 834 | @ (Property: "timeline-plaintext")</p> |
| 835 | |
| 836 | @ <hr> |
| 837 | onoff_attribute("Truncate comment at first blank line (Git-style)", |
| 838 | "timeline-truncate-at-blank", "ttb", 0, 0); |
| 839 | @ <p>In timeline displays, check-in comments are displayed only through |
| 840 | @ the first blank line. This is the traditional way to display comments |
| 841 | @ in Git repositories (Property: "timeline-truncate-at-blank")</p> |
| 842 | |
| 843 | @ <hr> |
| 844 | onoff_attribute("Break comments at newline characters", |
| 845 | "timeline-hard-newlines", "thnl", 0, 0); |
| 846 | @ <p>In timeline displays, newline characters in check-in comments force |
| 847 | @ a line break on the display. |
| 848 | @ (Property: "timeline-hard-newlines")</p> |
| 849 | |
| 850 | @ <hr> |
| 851 | onoff_attribute("Use Universal Coordinated Time (UTC)", |
| 852 | "timeline-utc", "utc", 1, 0); |
| 853 | @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or |
| 854 | @ Zulu) instead of in local time. On this server, local time is currently |
| 855 | tmDiff = db_double(0.0, "SELECT julianday('now')"); |
| @@ -866,40 +866,40 @@ | |
| 866 | }else{ |
| 867 | @ %s(zTmDiff) hours ahead of UTC.</p> |
| 868 | } |
| 869 | @ <p>(Property: "timeline-utc") |
| 870 | |
| 871 | @ <hr> |
| 872 | multiple_choice_attribute("Style", "timeline-default-style", |
| 873 | "tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles); |
| 874 | @ <p>The default timeline viewing style, for when the user has not |
| 875 | @ specified an alternative. (Property: "timeline-default-style")</p> |
| 876 | |
| 877 | @ <hr> |
| 878 | entry_attribute("Default Number Of Rows", 6, "timeline-default-length", |
| 879 | "tldl", "50", 0); |
| 880 | @ <p>The maximum number of rows to show on a timeline in the absence |
| 881 | @ of a user display preference cookie setting or an explicit n= query |
| 882 | @ parameter. (Property: "timeline-default-length")</p> |
| 883 | |
| 884 | @ <hr> |
| 885 | multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", |
| 886 | "tdf", "0", count(azTimeFormats)/2, azTimeFormats); |
| 887 | @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown |
| 888 | @ in a separate box (using CSS class "timelineDate") whenever the date |
| 889 | @ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, |
| 890 | @ the complete date and time is shown on every timeline entry using the |
| 891 | @ CSS class "timelineTime". (Property: "timeline-date-format")</p> |
| 892 | |
| 893 | @ <hr> |
| 894 | entry_attribute("Max timeline comment length", 6, |
| 895 | "timeline-max-comment", "tmc", "0", 0); |
| 896 | @ <p>The maximum length of a comment to be displayed in a timeline. |
| 897 | @ "0" there is no length limit. |
| 898 | @ (Property: "timeline-max-comment")</p> |
| 899 | |
| 900 | @ <hr> |
| 901 | entry_attribute("Tooltip dwell time (milliseconds)", 6, |
| 902 | "timeline-dwelltime", "tdt", "100", 0); |
| 903 | @ <br> |
| 904 | entry_attribute("Tooltip close time (milliseconds)", 6, |
| 905 | "timeline-closetime", "tct", "250", 0); |
| @@ -911,11 +911,11 @@ | |
| 911 | @ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br> |
| 912 | @ Set <strong>close time</strong> to "0" to keep tooltips visible until |
| 913 | @ the mouse is clicked elsewhere.<p> |
| 914 | @ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p> |
| 915 | |
| 916 | @ <hr> |
| 917 | onoff_attribute("Timestamp hyperlinks to /info", |
| 918 | "timeline-tslink-info", "ttlti", 0, 0); |
| 919 | @ <p>The hyperlink on the timestamp associated with each timeline entry, |
| 920 | @ on the far left-hand side of the screen, normally targets another |
| 921 | @ /timeline page that shows the entry in context. However, if this |
| @@ -927,12 +927,12 @@ | |
| 927 | @ double-clicking the timeline node or by clicking on the hash that |
| 928 | @ follows "check-in:" in the supplemental information section on the |
| 929 | @ right of the entry. |
| 930 | @ <p>(Properties: "timeline-tslink-info") |
| 931 | |
| 932 | @ <hr> |
| 933 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 934 | @ </div></form> |
| 935 | db_end_transaction(0); |
| 936 | style_finish_page(); |
| 937 | } |
| 938 | |
| @@ -964,11 +964,11 @@ | |
| 964 | db_begin_transaction(); |
| 965 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 966 | @ by the contents of managed files named |
| 967 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 968 | @ If the file for a versionable setting exists, the value cannot be |
| 969 | @ changed on this screen.</p><hr><p> |
| 970 | @ |
| 971 | @ <form action="%R/setup_settings" method="post"><div> |
| 972 | @ <table border="0"><tr><td valign="top"> |
| 973 | login_insert_csrf_secret(); |
| 974 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| @@ -978,17 +978,17 @@ | |
| 978 | onoff_attribute("", pSet->name, |
| 979 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 980 | is_truth(pSet->def), hasVersionableValue); |
| 981 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 982 | if( pSet->versionable ){ |
| 983 | @ (v)<br> |
| 984 | } else { |
| 985 | @ <br> |
| 986 | } |
| 987 | } |
| 988 | } |
| 989 | @ <br><input type="submit" name="submit" value="Apply Changes"> |
| 990 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 991 | @ <table> |
| 992 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 993 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 994 | int hasVersionableValue = pSet->versionable && |
| @@ -1012,18 +1012,18 @@ | |
| 1012 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1013 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1014 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1015 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1016 | if( pSet->versionable ){ |
| 1017 | @ (v)<br> |
| 1018 | } else { |
| 1019 | @ <br> |
| 1020 | } |
| 1021 | textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name, |
| 1022 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1023 | (char*)pSet->def, hasVersionableValue); |
| 1024 | @<br> |
| 1025 | } |
| 1026 | } |
| 1027 | @ </td></tr></table> |
| 1028 | @ </div></form> |
| 1029 | db_end_transaction(0); |
| @@ -1097,24 +1097,24 @@ | |
| 1097 | style_set_current_feature("setup"); |
| 1098 | style_header("WWW Configuration"); |
| 1099 | db_begin_transaction(); |
| 1100 | @ <form action="%R/setup_config" method="post"><div> |
| 1101 | login_insert_csrf_secret(); |
| 1102 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1103 | @ <hr> |
| 1104 | entry_attribute("Project Name", 60, "project-name", "pn", "", 0); |
| 1105 | @ <p>A brief project name so visitors know what this site is about. |
| 1106 | @ The project name will also be used as the RSS feed title. |
| 1107 | @ (Property: "project-name") |
| 1108 | @ </p> |
| 1109 | @ <hr> |
| 1110 | textarea_attribute("Project Description", 3, 80, |
| 1111 | "project-description", "pd", "", 0); |
| 1112 | @ <p>Describe your project. This will be used in page headers for search |
| 1113 | @ engines as well as a short RSS description. |
| 1114 | @ (Property: "project-description")</p> |
| 1115 | @ <hr> |
| 1116 | entry_attribute("Canonical Server URL", 40, "email-url", |
| 1117 | "eurl", "", 0); |
| 1118 | @ <p>This is the URL used to access this repository as a server. |
| 1119 | @ Other repositories use this URL to clone or sync against this repository. |
| 1120 | @ This is also the basename for hyperlinks included in email alert text. |
| @@ -1130,20 +1130,20 @@ | |
| 1130 | @ ZIP archive. For best results, keep this prefix brief and avoid special |
| 1131 | @ characters such as "/" and "\". |
| 1132 | @ If no tarball prefix is specified, then the full Project Name above is used. |
| 1133 | @ (Property: "short-project-name") |
| 1134 | @ </p> |
| 1135 | @ <hr> |
| 1136 | entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0); |
| 1137 | @ <p>The <a href='%R/download'>/download</a> page is designed to provide |
| 1138 | @ a convenient place for newbies |
| 1139 | @ to download a ZIP archive or a tarball of the project. By default, |
| 1140 | @ the latest trunk check-in is downloaded. Change this tag to something |
| 1141 | @ else (ex: release) to alter the behavior of the /download page. |
| 1142 | @ (Property: "download-tag") |
| 1143 | @ </p> |
| 1144 | @ <hr> |
| 1145 | entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0); |
| 1146 | @ <p>Enter the pathname of the page to display when the "Home" menu |
| 1147 | @ option is selected and when no pathname is |
| 1148 | @ specified in the URL. For example, if you visit the url:</p> |
| 1149 | @ |
| @@ -1220,12 +1220,12 @@ | |
| 1220 | @ <p>The default value is blank, meaning no added entries. |
| 1221 | @ (Property: sitemap-extra) |
| 1222 | @ <p> |
| 1223 | textarea_attribute("Custom Sitemap Entries", 8, 80, |
| 1224 | "sitemap-extra", "smextra", "", 0); |
| 1225 | @ <hr> |
| 1226 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1227 | @ </div></form> |
| 1228 | db_end_transaction(0); |
| 1229 | style_finish_page(); |
| 1230 | } |
| 1231 | |
| @@ -1244,12 +1244,12 @@ | |
| 1244 | style_set_current_feature("setup"); |
| 1245 | style_header("Wiki Configuration"); |
| 1246 | db_begin_transaction(); |
| 1247 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1248 | login_insert_csrf_secret(); |
| 1249 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1250 | @ <hr> |
| 1251 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1252 | "wiki-about", "wiki-about", 1, 0); |
| 1253 | @ <p> |
| 1254 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1255 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| @@ -1259,11 +1259,11 @@ | |
| 1259 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1260 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1261 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1262 | @ </ul> |
| 1263 | @ (Property: "wiki-about")</p> |
| 1264 | @ <hr> |
| 1265 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1266 | "safe-html", "safe-html", "", 0); |
| 1267 | @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be |
| 1268 | @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. |
| 1269 | @ This setting is a string where each character indicates a "type" of |
| @@ -1279,16 +1279,16 @@ | |
| 1279 | @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except |
| 1280 | @ in forum posts, make this setting be "<b>btw</b>". The default is an |
| 1281 | @ empty string which means that Fossil never allows Markdown documents |
| 1282 | @ to generate unsafe HTML. |
| 1283 | @ (Property: "safe-html")</p> |
| 1284 | @ <hr> |
| 1285 | @ The current interwiki tag map is as follows: |
| 1286 | interwiki_append_map_table(cgi_output_blob()); |
| 1287 | @ <p>Visit <a href="./intermap">%R/intermap</a> for details or to |
| 1288 | @ modify the interwiki tag map. |
| 1289 | @ <hr> |
| 1290 | onoff_attribute("Use HTML as wiki markup language", |
| 1291 | "wiki-use-html", "wiki-use-html", 0, 0); |
| 1292 | @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed |
| 1293 | @ but all other wiki formatting will be ignored.</p> |
| 1294 | @ <p><strong>CAUTION:</strong> when |
| @@ -1297,12 +1297,12 @@ | |
| 1297 | @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p> |
| 1298 | @ <p>This should <strong>only</strong> be enabled when wiki editing is limited |
| 1299 | @ to trusted users. It should <strong>not</strong> be used on a publicly |
| 1300 | @ editable wiki.</p> |
| 1301 | @ (Property: "wiki-use-html") |
| 1302 | @ <hr> |
| 1303 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1304 | @ </div></form> |
| 1305 | db_end_transaction(0); |
| 1306 | style_finish_page(); |
| 1307 | } |
| 1308 | |
| @@ -1328,32 +1328,32 @@ | |
| 1328 | style_set_current_feature("setup"); |
| 1329 | style_header("Chat Configuration"); |
| 1330 | db_begin_transaction(); |
| 1331 | @ <form action="%R/setup_chat" method="post"><div> |
| 1332 | login_insert_csrf_secret(); |
| 1333 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1334 | @ <hr> |
| 1335 | entry_attribute("Initial Chat History Size", 10, |
| 1336 | "chat-initial-history", "chatih", "50", 0); |
| 1337 | @ <p>When /chat first starts up, it preloads up to this many historical |
| 1338 | @ messages. |
| 1339 | @ (Property: "chat-initial-history")</p> |
| 1340 | @ <hr> |
| 1341 | entry_attribute("Minimum Number Of Historical Messages To Retain", 10, |
| 1342 | "chat-keep-count", "chatkc", "50", 0); |
| 1343 | @ <p>The chat subsystem purges older messages. But it will always retain |
| 1344 | @ the N most recent messages where N is the value of this setting. |
| 1345 | @ (Property: "chat-keep-count")</p> |
| 1346 | @ <hr> |
| 1347 | entry_attribute("Maximum Message Age In Days", 10, |
| 1348 | "chat-keep-days", "chatkd", "7", 0); |
| 1349 | @ <p>Chat message are removed after N days, where N is the value of |
| 1350 | @ this setting. N may be fractional. So, for example, to only keep |
| 1351 | @ an historical record of chat messages for 12 hours, set this value |
| 1352 | @ to 0.5. |
| 1353 | @ (Property: "chat-keep-days")</p> |
| 1354 | @ <hr> |
| 1355 | entry_attribute("Chat Polling Timeout", 10, |
| 1356 | "chat-poll-timeout", "chatpt", "420", 0); |
| 1357 | @ <p>New chat content is downloaded using the "long poll" technique. |
| 1358 | @ HTTP requests are made to /chat-poll which blocks waiting on new |
| 1359 | @ content to arrive. But the /chat-poll cannot block forever. It |
| @@ -1362,28 +1362,28 @@ | |
| 1362 | @ default setting of approximately 7 minutes works well on many systems. |
| 1363 | @ Shorter delays might be required on installations that use proxies |
| 1364 | @ or web-servers with short timeouts. For best efficiency, this value |
| 1365 | @ should be larger rather than smaller. |
| 1366 | @ (Property: "chat-poll-timeout")</p> |
| 1367 | @ <hr> |
| 1368 | entry_attribute("Chat Timeline Robot Username", 15, |
| 1369 | "chat-timeline-user", "chatrobot", "", 0); |
| 1370 | @ <p>If this setting is not an empty string, then any changes that appear |
| 1371 | @ on the timeline are announced in the chatroom under the username |
| 1372 | @ supplied. The username does not need to actually exist in the USER table. |
| 1373 | @ Suggested username: "chat-robot". |
| 1374 | @ (Property: "chat-timeline-user")</p> |
| 1375 | @ <hr> |
| 1376 | |
| 1377 | multiple_choice_attribute("Alert sound", |
| 1378 | "chat-alert-sound", "snd", azAlerts[0], |
| 1379 | count(azAlerts)/2, azAlerts); |
| 1380 | @ <p>The sound used in the client-side chat to indicate that a new |
| 1381 | @ chat message has arrived. |
| 1382 | @ (Property: "chat-alert-sound")</p> |
| 1383 | @ <hr/> |
| 1384 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1385 | @ </div></form> |
| 1386 | db_end_transaction(0); |
| 1387 | @ <script nonce="%h(style_nonce())"> |
| 1388 | @ (function(){ |
| 1389 | @ var w = document.getElementById('idsnd'); |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | style_set_current_feature("setup"); |
| 1413 | style_header("Moderator For Wiki And Tickets"); |
| 1414 | db_begin_transaction(); |
| 1415 | @ <form action="%R/setup_modreq" method="post"><div> |
| 1416 | login_insert_csrf_secret(); |
| 1417 | @ <hr> |
| 1418 | onoff_attribute("Moderate ticket changes", |
| 1419 | "modreq-tkt", "modreq-tkt", 0, 0); |
| 1420 | @ <p>When enabled, any change to tickets is subject to the approval |
| 1421 | @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. |
| 1422 | @ Ticket changes enter the system and are shown locally, but are not |
| @@ -1423,11 +1423,11 @@ | |
| 1423 | @ synced until they are approved. The moderator has the option to |
| 1424 | @ delete the change rather than approve it. Ticket changes made by |
| 1425 | @ a user who has the Mod-Tkt privilege are never subject to |
| 1426 | @ moderation. (Property: "modreq-tkt") |
| 1427 | @ |
| 1428 | @ <hr> |
| 1429 | onoff_attribute("Moderate wiki changes", |
| 1430 | "modreq-wiki", "modreq-wiki", 0, 0); |
| 1431 | @ <p>When enabled, any change to wiki is subject to the approval |
| 1432 | @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. |
| 1433 | @ Wiki changes enter the system and are shown locally, but are not |
| @@ -1435,12 +1435,12 @@ | |
| 1435 | @ delete the change rather than approve it. Wiki changes made by |
| 1436 | @ a user who has the Mod-Wiki privilege are never subject to |
| 1437 | @ moderation. (Property: "modreq-wiki") |
| 1438 | @ </p> |
| 1439 | |
| 1440 | @ <hr> |
| 1441 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1442 | @ </div></form> |
| 1443 | db_end_transaction(0); |
| 1444 | style_finish_page(); |
| 1445 | |
| 1446 | } |
| @@ -1469,29 +1469,29 @@ | |
| 1469 | |
| 1470 | style_set_current_feature("setup"); |
| 1471 | style_header("Edit Ad Unit"); |
| 1472 | @ <form action="%R/setup_adunit" method="post"><div> |
| 1473 | login_insert_csrf_secret(); |
| 1474 | @ <b>Banner Ad-Unit:</b><br> |
| 1475 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1476 | @ <br> |
| 1477 | @ <b>Right-Column Ad-Unit:</b><br> |
| 1478 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1479 | @ <br> |
| 1480 | onoff_attribute("Omit ads to administrator", |
| 1481 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1482 | @ <br> |
| 1483 | onoff_attribute("Omit ads to logged-in users", |
| 1484 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1485 | @ <br> |
| 1486 | onoff_attribute("Temporarily disable all ads", |
| 1487 | "adunit-disable", "oall", 0, 0); |
| 1488 | @ <br> |
| 1489 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 1490 | @ <input type="submit" name="clear" value="Delete Ad-Unit"> |
| 1491 | @ </div></form> |
| 1492 | @ <hr> |
| 1493 | @ <b>Ad-Unit Notes:</b><ul> |
| 1494 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1495 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1496 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1497 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is |
| @@ -1651,12 +1651,12 @@ | |
| 1651 | } |
| 1652 | style_set_current_feature("setup"); |
| 1653 | style_header("Edit Project Logo And Background"); |
| 1654 | @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> |
| 1655 | @ and looks like this:</p> |
| 1656 | @ <blockquote><p> |
| 1657 | @ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1"> |
| 1658 | @ </p></blockquote> |
| 1659 | @ |
| 1660 | @ <form action="%R/setup_logo" method="post" |
| 1661 | @ enctype="multipart/form-data"><div> |
| 1662 | @ <p>The logo is accessible to all users at this URL: |
| @@ -1665,22 +1665,22 @@ | |
| 1665 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1666 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1667 | @ To change the logo image, use the following form:</p> |
| 1668 | login_insert_csrf_secret(); |
| 1669 | @ Logo Image file: |
| 1670 | @ <input type="file" name="logoim" size="60" accept="image/*"> |
| 1671 | @ <p align="center"> |
| 1672 | @ <input type="submit" name="setlogo" value="Change Logo"> |
| 1673 | @ <input type="submit" name="clrlogo" value="Revert To Default"></p> |
| 1674 | @ <p>(Properties: "logo-image" and "logo-mimetype") |
| 1675 | @ </div></form> |
| 1676 | @ <hr> |
| 1677 | @ |
| 1678 | @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> |
| 1679 | @ and looks like this:</p> |
| 1680 | @ <blockquote><p><img src="%R/background/%z(zBgMtime)" \ |
| 1681 | @ alt="background" border=1> |
| 1682 | @ </p></blockquote> |
| 1683 | @ |
| 1684 | @ <form action="%R/setup_logo" method="post" |
| 1685 | @ enctype="multipart/form-data"><div> |
| 1686 | @ <p>The background image is accessible to all users at this URL: |
| @@ -1689,22 +1689,22 @@ | |
| 1689 | @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and |
| 1690 | @ <a href="setup_skinedit?w=2">header setup</a>. |
| 1691 | @ To change the background image, use the following form:</p> |
| 1692 | login_insert_csrf_secret(); |
| 1693 | @ Background image file: |
| 1694 | @ <input type="file" name="bgim" size="60" accept="image/*"> |
| 1695 | @ <p align="center"> |
| 1696 | @ <input type="submit" name="setbg" value="Change Background"> |
| 1697 | @ <input type="submit" name="clrbg" value="Revert To Default"></p> |
| 1698 | @ </div></form> |
| 1699 | @ <p>(Properties: "background-image" and "background-mimetype") |
| 1700 | @ <hr> |
| 1701 | @ |
| 1702 | @ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b> |
| 1703 | @ and looks like this:</p> |
| 1704 | @ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \ |
| 1705 | @ alt="icon" border=1> |
| 1706 | @ </p></blockquote> |
| 1707 | @ |
| 1708 | @ <form action="%R/setup_logo" method="post" |
| 1709 | @ enctype="multipart/form-data"><div> |
| 1710 | @ <p>The icon image is accessible to all users at this URL: |
| @@ -1713,17 +1713,17 @@ | |
| 1713 | @ page depending on the web browser in use and the MIME-Types that it |
| 1714 | @ supports for icon images. |
| 1715 | @ To change the icon image, use the following form:</p> |
| 1716 | login_insert_csrf_secret(); |
| 1717 | @ Icon image file: |
| 1718 | @ <input type="file" name="iconim" size="60" accept="image/*"> |
| 1719 | @ <p align="center"> |
| 1720 | @ <input type="submit" name="seticon" value="Change Icon"> |
| 1721 | @ <input type="submit" name="clricon" value="Revert To Default"></p> |
| 1722 | @ </div></form> |
| 1723 | @ <p>(Properties: "icon-image" and "icon-mimetype") |
| 1724 | @ <hr> |
| 1725 | @ |
| 1726 | @ <p><span class="note">Note:</span> Your browser has probably cached these |
| 1727 | @ images, so you may need to press the Reload button before changes will |
| 1728 | @ take effect. </p> |
| 1729 | style_finish_page(); |
| @@ -1804,12 +1804,12 @@ | |
| 1804 | go = 1; |
| 1805 | } |
| 1806 | @ |
| 1807 | @ <form method="post" action="%R/admin_sql"> |
| 1808 | login_insert_csrf_secret(); |
| 1809 | @ SQL:<br> |
| 1810 | @ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br> |
| 1811 | @ <input type="submit" name="go" value="Run SQL"> |
| 1812 | @ <input type="submit" name="schema" value="Show Schema"> |
| 1813 | @ <input type="submit" name="tablelist" value="List Tables"> |
| 1814 | @ <input type="submit" name="configtab" value="CONFIG Table Query"> |
| 1815 | @ </form> |
| @@ -1827,11 +1827,11 @@ | |
| 1827 | int rc; |
| 1828 | const char *zTail; |
| 1829 | int nCol; |
| 1830 | int nRow = 0; |
| 1831 | int i; |
| 1832 | @ <hr> |
| 1833 | login_verify_csrf_secret(); |
| 1834 | sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0); |
| 1835 | search_sql_setup(g.db); |
| 1836 | rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail); |
| 1837 | if( rc!=SQLITE_OK ){ |
| @@ -1913,19 +1913,19 @@ | |
| 1913 | @ run by this page. If Tcl integration was enabled at compile-time and |
| 1914 | @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> |
| 1915 | @ |
| 1916 | @ <form method="post" action="%R/admin_th1"> |
| 1917 | login_insert_csrf_secret(); |
| 1918 | @ TH1:<br> |
| 1919 | @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br> |
| 1920 | @ <input type="submit" name="go" value="Run TH1"> |
| 1921 | @ </form> |
| 1922 | if( go ){ |
| 1923 | const char *zR; |
| 1924 | int rc; |
| 1925 | int n; |
| 1926 | @ <hr> |
| 1927 | login_verify_csrf_secret(); |
| 1928 | rc = Th_Eval(g.interp, 0, zQ, -1); |
| 1929 | zR = Th_GetResult(g.interp, &n); |
| 1930 | if( rc==TH_OK ){ |
| 1931 | @ <pre class="th1result">%h(zR)</pre> |
| @@ -2005,10 +2005,25 @@ | |
| 2005 | @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p> |
| 2006 | } |
| 2007 | style_finish_page(); |
| 2008 | } |
| 2009 | |
| 2010 | |
| 2011 | /* |
| 2012 | ** Renders a selection list of values for the search-tokenizer |
| 2013 | ** setting, using the form field name "ftstok". |
| 2014 | */ |
| 2015 | static void select_fts_tokenizer(void){ |
| 2016 | const char *const aTokenizer[] = { |
| 2017 | "off", "None", |
| 2018 | "porter", "Porter Stemmer", |
| 2019 | "trigram", "Trigram" |
| 2020 | }; |
| 2021 | multiple_choice_attribute("FTS Tokenizer", "search-tokenizer", |
| 2022 | "ftstok", "off", 3, aTokenizer); |
| 2023 | } |
| 2024 | |
| 2025 | /* |
| 2026 | ** WEBPAGE: srchsetup |
| 2027 | ** |
| 2028 | ** Configure the search engine. Requires Admin privilege. |
| 2029 | */ |
| @@ -2024,11 +2039,11 @@ | |
| 2039 | login_insert_csrf_secret(); |
| 2040 | @ <div style="text-align:center;font-weight:bold;"> |
| 2041 | @ Server-specific settings that affect the |
| 2042 | @ <a href="%R/search">/search</a> webpage. |
| 2043 | @ </div> |
| 2044 | @ <hr> |
| 2045 | textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); |
| 2046 | @ <p>The "Document Glob List" is a comma- or newline-separated list |
| 2047 | @ of GLOB expressions that identify all documents within the source |
| 2048 | @ tree that are to be searched when "Document Search" is enabled. |
| 2049 | @ Some examples: |
| @@ -2040,51 +2055,53 @@ | |
| 2055 | @ files.</tr> |
| 2056 | @ <tr><td>*<td><td>Search all checked-in files</tr> |
| 2057 | @ <tr><td><i>(blank)</i><td> |
| 2058 | @ <td>Search nothing. (Disables document search).</tr> |
| 2059 | @ </table> |
| 2060 | @ <hr> |
| 2061 | entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); |
| 2062 | @ <p>When searching documents, use the versions of the files found at the |
| 2063 | @ type of the "Document Branch" branch. Recommended value: "trunk". |
| 2064 | @ Document search is disabled if blank. |
| 2065 | @ <hr> |
| 2066 | onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); |
| 2067 | @ <br> |
| 2068 | onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); |
| 2069 | @ <br> |
| 2070 | onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); |
| 2071 | @ <br> |
| 2072 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2073 | @ <br> |
| 2074 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2075 | @ <br> |
| 2076 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2077 | @ <hr> |
| 2078 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 2079 | @ <hr> |
| 2080 | if( P("fts0") ){ |
| 2081 | search_drop_index(); |
| 2082 | }else if( P("fts1") ){ |
| 2083 | const char *zTokenizer = PD("ftstok","off"); |
| 2084 | search_set_tokenizer(zTokenizer); |
| 2085 | search_drop_index(); |
| 2086 | search_create_index(); |
| 2087 | search_fill_index(); |
| 2088 | search_update_index(search_restrict(SRCH_ALL)); |
| 2089 | } |
| 2090 | if( search_index_exists() ){ |
| 2091 | @ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index. |
| 2092 | @ The index helps search run faster, especially on large repositories, |
| 2093 | @ but takes up space.</p> |
| 2094 | select_fts_tokenizer(); |
| 2095 | @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> |
| 2096 | @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> |
| 2097 | style_submenu_element("FTS Index Debugging","%R/test-ftsdocs"); |
| 2098 | }else{ |
| 2099 | @ <p>The SQLite search index is disabled. All searching will be |
| 2100 | @ a full-text scan. This usually works fine, but can be slow for |
| 2101 | @ larger repositories.</p> |
| 2102 | select_fts_tokenizer(); |
| 2103 | @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> |
| 2104 | } |
| 2105 | @ </div></form> |
| 2106 | style_finish_page(); |
| 2107 | } |
| 2108 |
+40
-40
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -611,20 +611,20 @@ | ||
| 611 | 611 | @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ |
| 612 | 612 | @ name="id" value="%d(uid)"/>\ |
| 613 | 613 | @ </td> |
| 614 | 614 | }else{ |
| 615 | 615 | @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ |
| 616 | - @ value="0" /></td> | |
| 616 | + @ value="0"></td> | |
| 617 | 617 | } |
| 618 | 618 | @ </tr> |
| 619 | 619 | @ <tr> |
| 620 | 620 | @ <td class="usetupEditLabel" id="sulgn">Login:</td> |
| 621 | 621 | if( login_is_special(zLogin) ){ |
| 622 | 622 | @ <td><b>%h(zLogin)</b></td> |
| 623 | 623 | }else{ |
| 624 | 624 | @ <td><input aria-labelledby="sulgn" type="text" name="login" \ |
| 625 | - @ value="%h(zLogin)" /> | |
| 625 | + @ value="%h(zLogin)"> | |
| 626 | 626 | if( alert_tables_exist() ){ |
| 627 | 627 | int sid; |
| 628 | 628 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 629 | 629 | " WHERE suname=%Q", zLogin); |
| 630 | 630 | if( sid>0 ){ |
| @@ -644,82 +644,82 @@ | ||
| 644 | 644 | @ <td width="100%%"> |
| 645 | 645 | #define B(x) inherit[x] |
| 646 | 646 | @ <div class="columns" style="column-width:13em;"> |
| 647 | 647 | @ <ul style="list-style-type: none;"> |
| 648 | 648 | if( g.perm.Setup ){ |
| 649 | - @ <li><label><input type="checkbox" name="as"%s(oa['s']) /> | |
| 649 | + @ <li><label><input type="checkbox" name="as"%s(oa['s'])> | |
| 650 | 650 | @ Setup%s(B('s'))</label> |
| 651 | 651 | } |
| 652 | - @ <li><label><input type="checkbox" name="aa"%s(oa['a']) /> | |
| 652 | + @ <li><label><input type="checkbox" name="aa"%s(oa['a'])> | |
| 653 | 653 | @ Admin%s(B('a'))</label> |
| 654 | - @ <li><label><input type="checkbox" name="au"%s(oa['u']) /> | |
| 654 | + @ <li><label><input type="checkbox" name="au"%s(oa['u'])> | |
| 655 | 655 | @ Reader%s(B('u'))</label> |
| 656 | - @ <li><label><input type="checkbox" name="av"%s(oa['v']) /> | |
| 656 | + @ <li><label><input type="checkbox" name="av"%s(oa['v'])> | |
| 657 | 657 | @ Developer%s(B('v'))</label> |
| 658 | 658 | #if 0 /* Not Used */ |
| 659 | - @ <li><label><input type="checkbox" name="ad"%s(oa['d']) /> | |
| 659 | + @ <li><label><input type="checkbox" name="ad"%s(oa['d'])> | |
| 660 | 660 | @ Delete%s(B('d'))</label> |
| 661 | 661 | #endif |
| 662 | - @ <li><label><input type="checkbox" name="ae"%s(oa['e']) /> | |
| 662 | + @ <li><label><input type="checkbox" name="ae"%s(oa['e'])> | |
| 663 | 663 | @ View-PII%s(B('e'))</label> |
| 664 | - @ <li><label><input type="checkbox" name="ap"%s(oa['p']) /> | |
| 664 | + @ <li><label><input type="checkbox" name="ap"%s(oa['p'])> | |
| 665 | 665 | @ Password%s(B('p'))</label> |
| 666 | - @ <li><label><input type="checkbox" name="ai"%s(oa['i']) /> | |
| 666 | + @ <li><label><input type="checkbox" name="ai"%s(oa['i'])> | |
| 667 | 667 | @ Check-In%s(B('i'))</label> |
| 668 | - @ <li><label><input type="checkbox" name="ao"%s(oa['o']) /> | |
| 668 | + @ <li><label><input type="checkbox" name="ao"%s(oa['o'])> | |
| 669 | 669 | @ Check-Out%s(B('o'))</label> |
| 670 | - @ <li><label><input type="checkbox" name="ah"%s(oa['h']) /> | |
| 670 | + @ <li><label><input type="checkbox" name="ah"%s(oa['h'])> | |
| 671 | 671 | @ Hyperlinks%s(B('h'))</label> |
| 672 | - @ <li><label><input type="checkbox" name="ab"%s(oa['b']) /> | |
| 672 | + @ <li><label><input type="checkbox" name="ab"%s(oa['b'])> | |
| 673 | 673 | @ Attachments%s(B('b'))</label> |
| 674 | - @ <li><label><input type="checkbox" name="ag"%s(oa['g']) /> | |
| 674 | + @ <li><label><input type="checkbox" name="ag"%s(oa['g'])> | |
| 675 | 675 | @ Clone%s(B('g'))</label> |
| 676 | - @ <li><label><input type="checkbox" name="aj"%s(oa['j']) /> | |
| 676 | + @ <li><label><input type="checkbox" name="aj"%s(oa['j'])> | |
| 677 | 677 | @ Read Wiki%s(B('j'))</label> |
| 678 | - @ <li><label><input type="checkbox" name="af"%s(oa['f']) /> | |
| 678 | + @ <li><label><input type="checkbox" name="af"%s(oa['f'])> | |
| 679 | 679 | @ New Wiki%s(B('f'))</label> |
| 680 | - @ <li><label><input type="checkbox" name="am"%s(oa['m']) /> | |
| 680 | + @ <li><label><input type="checkbox" name="am"%s(oa['m'])> | |
| 681 | 681 | @ Append Wiki%s(B('m'))</label> |
| 682 | - @ <li><label><input type="checkbox" name="ak"%s(oa['k']) /> | |
| 682 | + @ <li><label><input type="checkbox" name="ak"%s(oa['k'])> | |
| 683 | 683 | @ Write Wiki%s(B('k'))</label> |
| 684 | - @ <li><label><input type="checkbox" name="al"%s(oa['l']) /> | |
| 684 | + @ <li><label><input type="checkbox" name="al"%s(oa['l'])> | |
| 685 | 685 | @ Moderate Wiki%s(B('l'))</label> |
| 686 | - @ <li><label><input type="checkbox" name="ar"%s(oa['r']) /> | |
| 686 | + @ <li><label><input type="checkbox" name="ar"%s(oa['r'])> | |
| 687 | 687 | @ Read Ticket%s(B('r'))</label> |
| 688 | - @ <li><label><input type="checkbox" name="an"%s(oa['n']) /> | |
| 688 | + @ <li><label><input type="checkbox" name="an"%s(oa['n'])> | |
| 689 | 689 | @ New Tickets%s(B('n'))</label> |
| 690 | - @ <li><label><input type="checkbox" name="ac"%s(oa['c']) /> | |
| 690 | + @ <li><label><input type="checkbox" name="ac"%s(oa['c'])> | |
| 691 | 691 | @ Append To Ticket%s(B('c'))</label> |
| 692 | - @ <li><label><input type="checkbox" name="aw"%s(oa['w']) /> | |
| 692 | + @ <li><label><input type="checkbox" name="aw"%s(oa['w'])> | |
| 693 | 693 | @ Write Tickets%s(B('w'))</label> |
| 694 | - @ <li><label><input type="checkbox" name="aq"%s(oa['q']) /> | |
| 694 | + @ <li><label><input type="checkbox" name="aq"%s(oa['q'])> | |
| 695 | 695 | @ Moderate Tickets%s(B('q'))</label> |
| 696 | - @ <li><label><input type="checkbox" name="at"%s(oa['t']) /> | |
| 696 | + @ <li><label><input type="checkbox" name="at"%s(oa['t'])> | |
| 697 | 697 | @ Ticket Report%s(B('t'))</label> |
| 698 | - @ <li><label><input type="checkbox" name="ax"%s(oa['x']) /> | |
| 698 | + @ <li><label><input type="checkbox" name="ax"%s(oa['x'])> | |
| 699 | 699 | @ Private%s(B('x'))</label> |
| 700 | - @ <li><label><input type="checkbox" name="ay"%s(oa['y']) /> | |
| 700 | + @ <li><label><input type="checkbox" name="ay"%s(oa['y'])> | |
| 701 | 701 | @ Write Unversioned%s(B('y'))</label> |
| 702 | - @ <li><label><input type="checkbox" name="az"%s(oa['z']) /> | |
| 702 | + @ <li><label><input type="checkbox" name="az"%s(oa['z'])> | |
| 703 | 703 | @ Download Zip%s(B('z'))</label> |
| 704 | - @ <li><label><input type="checkbox" name="a2"%s(oa['2']) /> | |
| 704 | + @ <li><label><input type="checkbox" name="a2"%s(oa['2'])> | |
| 705 | 705 | @ Read Forum%s(B('2'))</label> |
| 706 | - @ <li><label><input type="checkbox" name="a3"%s(oa['3']) /> | |
| 706 | + @ <li><label><input type="checkbox" name="a3"%s(oa['3'])> | |
| 707 | 707 | @ Write Forum%s(B('3'))</label> |
| 708 | - @ <li><label><input type="checkbox" name="a4"%s(oa['4']) /> | |
| 708 | + @ <li><label><input type="checkbox" name="a4"%s(oa['4'])> | |
| 709 | 709 | @ WriteTrusted Forum%s(B('4'))</label> |
| 710 | - @ <li><label><input type="checkbox" name="a5"%s(oa['5']) /> | |
| 710 | + @ <li><label><input type="checkbox" name="a5"%s(oa['5'])> | |
| 711 | 711 | @ Moderate Forum%s(B('5'))</label> |
| 712 | - @ <li><label><input type="checkbox" name="a6"%s(oa['6']) /> | |
| 712 | + @ <li><label><input type="checkbox" name="a6"%s(oa['6'])> | |
| 713 | 713 | @ Supervise Forum%s(B('6'))</label> |
| 714 | - @ <li><label><input type="checkbox" name="a7"%s(oa['7']) /> | |
| 714 | + @ <li><label><input type="checkbox" name="a7"%s(oa['7'])> | |
| 715 | 715 | @ Email Alerts%s(B('7'))</label> |
| 716 | - @ <li><label><input type="checkbox" name="aA"%s(oa['A']) /> | |
| 716 | + @ <li><label><input type="checkbox" name="aA"%s(oa['A'])> | |
| 717 | 717 | @ Send Announcements%s(B('A'))</label> |
| 718 | - @ <li><label><input type="checkbox" name="aC"%s(oa['C']) /> | |
| 718 | + @ <li><label><input type="checkbox" name="aC"%s(oa['C'])> | |
| 719 | 719 | @ Chatroom%s(B('C'))</label> |
| 720 | - @ <li><label><input type="checkbox" name="aD"%s(oa['D']) /> | |
| 720 | + @ <li><label><input type="checkbox" name="aD"%s(oa['D'])> | |
| 721 | 721 | @ Enable Debug%s(B('D'))</label> |
| 722 | 722 | @ </ul></div> |
| 723 | 723 | @ </td> |
| 724 | 724 | @ </tr> |
| 725 | 725 | @ <tr> |
| @@ -733,27 +733,27 @@ | ||
| 733 | 733 | @ <tr> |
| 734 | 734 | @ <td align="right" id="supw">Password:</td> |
| 735 | 735 | if( zPw[0] ){ |
| 736 | 736 | /* Obscure the password for all users */ |
| 737 | 737 | @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ |
| 738 | - @ name="pw" value="**********" /> | |
| 738 | + @ name="pw" value="**********"> | |
| 739 | 739 | @ (Leave unchanged to retain password)</td> |
| 740 | 740 | }else{ |
| 741 | 741 | /* Show an empty password as an empty input field */ |
| 742 | 742 | char *zRPW = fossil_random_password(12); |
| 743 | 743 | @ <td><input aria-labelledby="supw" type="password" name="pw" \ |
| 744 | - @ autocomplete="off" value="" /> Password suggestion: %z(zRPW)</td> | |
| 744 | + @ autocomplete="off" value=""> Password suggestion: %z(zRPW)</td> | |
| 745 | 745 | } |
| 746 | 746 | @ </tr> |
| 747 | 747 | } |
| 748 | 748 | zGroup = login_group_name(); |
| 749 | 749 | if( zGroup ){ |
| 750 | 750 | @ <tr> |
| 751 | 751 | @ <td valign="top" align="right">Scope:</td> |
| 752 | 752 | @ <td valign="top"> |
| 753 | 753 | @ <input type="radio" name="all" checked value="0"> |
| 754 | - @ Apply changes to this repository only.<br /> | |
| 754 | + @ Apply changes to this repository only.<br> | |
| 755 | 755 | @ <input type="radio" name="all" value="1"> |
| 756 | 756 | @ Apply changes to all repositories in the "<b>%h(zGroup)</b>" |
| 757 | 757 | @ login group.</td></tr> |
| 758 | 758 | } |
| 759 | 759 | if( !higherUser ){ |
| 760 | 760 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -611,20 +611,20 @@ | |
| 611 | @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ |
| 612 | @ name="id" value="%d(uid)"/>\ |
| 613 | @ </td> |
| 614 | }else{ |
| 615 | @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ |
| 616 | @ value="0" /></td> |
| 617 | } |
| 618 | @ </tr> |
| 619 | @ <tr> |
| 620 | @ <td class="usetupEditLabel" id="sulgn">Login:</td> |
| 621 | if( login_is_special(zLogin) ){ |
| 622 | @ <td><b>%h(zLogin)</b></td> |
| 623 | }else{ |
| 624 | @ <td><input aria-labelledby="sulgn" type="text" name="login" \ |
| 625 | @ value="%h(zLogin)" /> |
| 626 | if( alert_tables_exist() ){ |
| 627 | int sid; |
| 628 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 629 | " WHERE suname=%Q", zLogin); |
| 630 | if( sid>0 ){ |
| @@ -644,82 +644,82 @@ | |
| 644 | @ <td width="100%%"> |
| 645 | #define B(x) inherit[x] |
| 646 | @ <div class="columns" style="column-width:13em;"> |
| 647 | @ <ul style="list-style-type: none;"> |
| 648 | if( g.perm.Setup ){ |
| 649 | @ <li><label><input type="checkbox" name="as"%s(oa['s']) /> |
| 650 | @ Setup%s(B('s'))</label> |
| 651 | } |
| 652 | @ <li><label><input type="checkbox" name="aa"%s(oa['a']) /> |
| 653 | @ Admin%s(B('a'))</label> |
| 654 | @ <li><label><input type="checkbox" name="au"%s(oa['u']) /> |
| 655 | @ Reader%s(B('u'))</label> |
| 656 | @ <li><label><input type="checkbox" name="av"%s(oa['v']) /> |
| 657 | @ Developer%s(B('v'))</label> |
| 658 | #if 0 /* Not Used */ |
| 659 | @ <li><label><input type="checkbox" name="ad"%s(oa['d']) /> |
| 660 | @ Delete%s(B('d'))</label> |
| 661 | #endif |
| 662 | @ <li><label><input type="checkbox" name="ae"%s(oa['e']) /> |
| 663 | @ View-PII%s(B('e'))</label> |
| 664 | @ <li><label><input type="checkbox" name="ap"%s(oa['p']) /> |
| 665 | @ Password%s(B('p'))</label> |
| 666 | @ <li><label><input type="checkbox" name="ai"%s(oa['i']) /> |
| 667 | @ Check-In%s(B('i'))</label> |
| 668 | @ <li><label><input type="checkbox" name="ao"%s(oa['o']) /> |
| 669 | @ Check-Out%s(B('o'))</label> |
| 670 | @ <li><label><input type="checkbox" name="ah"%s(oa['h']) /> |
| 671 | @ Hyperlinks%s(B('h'))</label> |
| 672 | @ <li><label><input type="checkbox" name="ab"%s(oa['b']) /> |
| 673 | @ Attachments%s(B('b'))</label> |
| 674 | @ <li><label><input type="checkbox" name="ag"%s(oa['g']) /> |
| 675 | @ Clone%s(B('g'))</label> |
| 676 | @ <li><label><input type="checkbox" name="aj"%s(oa['j']) /> |
| 677 | @ Read Wiki%s(B('j'))</label> |
| 678 | @ <li><label><input type="checkbox" name="af"%s(oa['f']) /> |
| 679 | @ New Wiki%s(B('f'))</label> |
| 680 | @ <li><label><input type="checkbox" name="am"%s(oa['m']) /> |
| 681 | @ Append Wiki%s(B('m'))</label> |
| 682 | @ <li><label><input type="checkbox" name="ak"%s(oa['k']) /> |
| 683 | @ Write Wiki%s(B('k'))</label> |
| 684 | @ <li><label><input type="checkbox" name="al"%s(oa['l']) /> |
| 685 | @ Moderate Wiki%s(B('l'))</label> |
| 686 | @ <li><label><input type="checkbox" name="ar"%s(oa['r']) /> |
| 687 | @ Read Ticket%s(B('r'))</label> |
| 688 | @ <li><label><input type="checkbox" name="an"%s(oa['n']) /> |
| 689 | @ New Tickets%s(B('n'))</label> |
| 690 | @ <li><label><input type="checkbox" name="ac"%s(oa['c']) /> |
| 691 | @ Append To Ticket%s(B('c'))</label> |
| 692 | @ <li><label><input type="checkbox" name="aw"%s(oa['w']) /> |
| 693 | @ Write Tickets%s(B('w'))</label> |
| 694 | @ <li><label><input type="checkbox" name="aq"%s(oa['q']) /> |
| 695 | @ Moderate Tickets%s(B('q'))</label> |
| 696 | @ <li><label><input type="checkbox" name="at"%s(oa['t']) /> |
| 697 | @ Ticket Report%s(B('t'))</label> |
| 698 | @ <li><label><input type="checkbox" name="ax"%s(oa['x']) /> |
| 699 | @ Private%s(B('x'))</label> |
| 700 | @ <li><label><input type="checkbox" name="ay"%s(oa['y']) /> |
| 701 | @ Write Unversioned%s(B('y'))</label> |
| 702 | @ <li><label><input type="checkbox" name="az"%s(oa['z']) /> |
| 703 | @ Download Zip%s(B('z'))</label> |
| 704 | @ <li><label><input type="checkbox" name="a2"%s(oa['2']) /> |
| 705 | @ Read Forum%s(B('2'))</label> |
| 706 | @ <li><label><input type="checkbox" name="a3"%s(oa['3']) /> |
| 707 | @ Write Forum%s(B('3'))</label> |
| 708 | @ <li><label><input type="checkbox" name="a4"%s(oa['4']) /> |
| 709 | @ WriteTrusted Forum%s(B('4'))</label> |
| 710 | @ <li><label><input type="checkbox" name="a5"%s(oa['5']) /> |
| 711 | @ Moderate Forum%s(B('5'))</label> |
| 712 | @ <li><label><input type="checkbox" name="a6"%s(oa['6']) /> |
| 713 | @ Supervise Forum%s(B('6'))</label> |
| 714 | @ <li><label><input type="checkbox" name="a7"%s(oa['7']) /> |
| 715 | @ Email Alerts%s(B('7'))</label> |
| 716 | @ <li><label><input type="checkbox" name="aA"%s(oa['A']) /> |
| 717 | @ Send Announcements%s(B('A'))</label> |
| 718 | @ <li><label><input type="checkbox" name="aC"%s(oa['C']) /> |
| 719 | @ Chatroom%s(B('C'))</label> |
| 720 | @ <li><label><input type="checkbox" name="aD"%s(oa['D']) /> |
| 721 | @ Enable Debug%s(B('D'))</label> |
| 722 | @ </ul></div> |
| 723 | @ </td> |
| 724 | @ </tr> |
| 725 | @ <tr> |
| @@ -733,27 +733,27 @@ | |
| 733 | @ <tr> |
| 734 | @ <td align="right" id="supw">Password:</td> |
| 735 | if( zPw[0] ){ |
| 736 | /* Obscure the password for all users */ |
| 737 | @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ |
| 738 | @ name="pw" value="**********" /> |
| 739 | @ (Leave unchanged to retain password)</td> |
| 740 | }else{ |
| 741 | /* Show an empty password as an empty input field */ |
| 742 | char *zRPW = fossil_random_password(12); |
| 743 | @ <td><input aria-labelledby="supw" type="password" name="pw" \ |
| 744 | @ autocomplete="off" value="" /> Password suggestion: %z(zRPW)</td> |
| 745 | } |
| 746 | @ </tr> |
| 747 | } |
| 748 | zGroup = login_group_name(); |
| 749 | if( zGroup ){ |
| 750 | @ <tr> |
| 751 | @ <td valign="top" align="right">Scope:</td> |
| 752 | @ <td valign="top"> |
| 753 | @ <input type="radio" name="all" checked value="0"> |
| 754 | @ Apply changes to this repository only.<br /> |
| 755 | @ <input type="radio" name="all" value="1"> |
| 756 | @ Apply changes to all repositories in the "<b>%h(zGroup)</b>" |
| 757 | @ login group.</td></tr> |
| 758 | } |
| 759 | if( !higherUser ){ |
| 760 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -611,20 +611,20 @@ | |
| 611 | @ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \ |
| 612 | @ name="id" value="%d(uid)"/>\ |
| 613 | @ </td> |
| 614 | }else{ |
| 615 | @ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \ |
| 616 | @ value="0"></td> |
| 617 | } |
| 618 | @ </tr> |
| 619 | @ <tr> |
| 620 | @ <td class="usetupEditLabel" id="sulgn">Login:</td> |
| 621 | if( login_is_special(zLogin) ){ |
| 622 | @ <td><b>%h(zLogin)</b></td> |
| 623 | }else{ |
| 624 | @ <td><input aria-labelledby="sulgn" type="text" name="login" \ |
| 625 | @ value="%h(zLogin)"> |
| 626 | if( alert_tables_exist() ){ |
| 627 | int sid; |
| 628 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 629 | " WHERE suname=%Q", zLogin); |
| 630 | if( sid>0 ){ |
| @@ -644,82 +644,82 @@ | |
| 644 | @ <td width="100%%"> |
| 645 | #define B(x) inherit[x] |
| 646 | @ <div class="columns" style="column-width:13em;"> |
| 647 | @ <ul style="list-style-type: none;"> |
| 648 | if( g.perm.Setup ){ |
| 649 | @ <li><label><input type="checkbox" name="as"%s(oa['s'])> |
| 650 | @ Setup%s(B('s'))</label> |
| 651 | } |
| 652 | @ <li><label><input type="checkbox" name="aa"%s(oa['a'])> |
| 653 | @ Admin%s(B('a'))</label> |
| 654 | @ <li><label><input type="checkbox" name="au"%s(oa['u'])> |
| 655 | @ Reader%s(B('u'))</label> |
| 656 | @ <li><label><input type="checkbox" name="av"%s(oa['v'])> |
| 657 | @ Developer%s(B('v'))</label> |
| 658 | #if 0 /* Not Used */ |
| 659 | @ <li><label><input type="checkbox" name="ad"%s(oa['d'])> |
| 660 | @ Delete%s(B('d'))</label> |
| 661 | #endif |
| 662 | @ <li><label><input type="checkbox" name="ae"%s(oa['e'])> |
| 663 | @ View-PII%s(B('e'))</label> |
| 664 | @ <li><label><input type="checkbox" name="ap"%s(oa['p'])> |
| 665 | @ Password%s(B('p'))</label> |
| 666 | @ <li><label><input type="checkbox" name="ai"%s(oa['i'])> |
| 667 | @ Check-In%s(B('i'))</label> |
| 668 | @ <li><label><input type="checkbox" name="ao"%s(oa['o'])> |
| 669 | @ Check-Out%s(B('o'))</label> |
| 670 | @ <li><label><input type="checkbox" name="ah"%s(oa['h'])> |
| 671 | @ Hyperlinks%s(B('h'))</label> |
| 672 | @ <li><label><input type="checkbox" name="ab"%s(oa['b'])> |
| 673 | @ Attachments%s(B('b'))</label> |
| 674 | @ <li><label><input type="checkbox" name="ag"%s(oa['g'])> |
| 675 | @ Clone%s(B('g'))</label> |
| 676 | @ <li><label><input type="checkbox" name="aj"%s(oa['j'])> |
| 677 | @ Read Wiki%s(B('j'))</label> |
| 678 | @ <li><label><input type="checkbox" name="af"%s(oa['f'])> |
| 679 | @ New Wiki%s(B('f'))</label> |
| 680 | @ <li><label><input type="checkbox" name="am"%s(oa['m'])> |
| 681 | @ Append Wiki%s(B('m'))</label> |
| 682 | @ <li><label><input type="checkbox" name="ak"%s(oa['k'])> |
| 683 | @ Write Wiki%s(B('k'))</label> |
| 684 | @ <li><label><input type="checkbox" name="al"%s(oa['l'])> |
| 685 | @ Moderate Wiki%s(B('l'))</label> |
| 686 | @ <li><label><input type="checkbox" name="ar"%s(oa['r'])> |
| 687 | @ Read Ticket%s(B('r'))</label> |
| 688 | @ <li><label><input type="checkbox" name="an"%s(oa['n'])> |
| 689 | @ New Tickets%s(B('n'))</label> |
| 690 | @ <li><label><input type="checkbox" name="ac"%s(oa['c'])> |
| 691 | @ Append To Ticket%s(B('c'))</label> |
| 692 | @ <li><label><input type="checkbox" name="aw"%s(oa['w'])> |
| 693 | @ Write Tickets%s(B('w'))</label> |
| 694 | @ <li><label><input type="checkbox" name="aq"%s(oa['q'])> |
| 695 | @ Moderate Tickets%s(B('q'))</label> |
| 696 | @ <li><label><input type="checkbox" name="at"%s(oa['t'])> |
| 697 | @ Ticket Report%s(B('t'))</label> |
| 698 | @ <li><label><input type="checkbox" name="ax"%s(oa['x'])> |
| 699 | @ Private%s(B('x'))</label> |
| 700 | @ <li><label><input type="checkbox" name="ay"%s(oa['y'])> |
| 701 | @ Write Unversioned%s(B('y'))</label> |
| 702 | @ <li><label><input type="checkbox" name="az"%s(oa['z'])> |
| 703 | @ Download Zip%s(B('z'))</label> |
| 704 | @ <li><label><input type="checkbox" name="a2"%s(oa['2'])> |
| 705 | @ Read Forum%s(B('2'))</label> |
| 706 | @ <li><label><input type="checkbox" name="a3"%s(oa['3'])> |
| 707 | @ Write Forum%s(B('3'))</label> |
| 708 | @ <li><label><input type="checkbox" name="a4"%s(oa['4'])> |
| 709 | @ WriteTrusted Forum%s(B('4'))</label> |
| 710 | @ <li><label><input type="checkbox" name="a5"%s(oa['5'])> |
| 711 | @ Moderate Forum%s(B('5'))</label> |
| 712 | @ <li><label><input type="checkbox" name="a6"%s(oa['6'])> |
| 713 | @ Supervise Forum%s(B('6'))</label> |
| 714 | @ <li><label><input type="checkbox" name="a7"%s(oa['7'])> |
| 715 | @ Email Alerts%s(B('7'))</label> |
| 716 | @ <li><label><input type="checkbox" name="aA"%s(oa['A'])> |
| 717 | @ Send Announcements%s(B('A'))</label> |
| 718 | @ <li><label><input type="checkbox" name="aC"%s(oa['C'])> |
| 719 | @ Chatroom%s(B('C'))</label> |
| 720 | @ <li><label><input type="checkbox" name="aD"%s(oa['D'])> |
| 721 | @ Enable Debug%s(B('D'))</label> |
| 722 | @ </ul></div> |
| 723 | @ </td> |
| 724 | @ </tr> |
| 725 | @ <tr> |
| @@ -733,27 +733,27 @@ | |
| 733 | @ <tr> |
| 734 | @ <td align="right" id="supw">Password:</td> |
| 735 | if( zPw[0] ){ |
| 736 | /* Obscure the password for all users */ |
| 737 | @ <td><input aria-labelledby="supw" type="password" autocomplete="off" \ |
| 738 | @ name="pw" value="**********"> |
| 739 | @ (Leave unchanged to retain password)</td> |
| 740 | }else{ |
| 741 | /* Show an empty password as an empty input field */ |
| 742 | char *zRPW = fossil_random_password(12); |
| 743 | @ <td><input aria-labelledby="supw" type="password" name="pw" \ |
| 744 | @ autocomplete="off" value=""> Password suggestion: %z(zRPW)</td> |
| 745 | } |
| 746 | @ </tr> |
| 747 | } |
| 748 | zGroup = login_group_name(); |
| 749 | if( zGroup ){ |
| 750 | @ <tr> |
| 751 | @ <td valign="top" align="right">Scope:</td> |
| 752 | @ <td valign="top"> |
| 753 | @ <input type="radio" name="all" checked value="0"> |
| 754 | @ Apply changes to this repository only.<br> |
| 755 | @ <input type="radio" name="all" value="1"> |
| 756 | @ Apply changes to all repositories in the "<b>%h(zGroup)</b>" |
| 757 | @ login group.</td></tr> |
| 758 | } |
| 759 | if( !higherUser ){ |
| 760 |
+15
-15
| --- src/shun.c | ||
| +++ src/shun.c | ||
| @@ -111,19 +111,19 @@ | ||
| 111 | 111 | } |
| 112 | 112 | admin_log("Unshunned %Q", p); |
| 113 | 113 | p += strlen(p)+1; |
| 114 | 114 | } |
| 115 | 115 | if( allExist ){ |
| 116 | - @ <p class="noMoreShun">Artifact(s)<br /> | |
| 116 | + @ <p class="noMoreShun">Artifact(s)<br> | |
| 117 | 117 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 118 | - @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> | |
| 118 | + @ <a href="%R/artifact/%s(p)">%s(p)</a><br> | |
| 119 | 119 | } |
| 120 | 120 | @ are no longer being shunned.</p> |
| 121 | 121 | }else{ |
| 122 | - @ <p class="noMoreShun">Artifact(s)<br /> | |
| 122 | + @ <p class="noMoreShun">Artifact(s)<br> | |
| 123 | 123 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 124 | - @ %s(p)<br /> | |
| 124 | + @ %s(p)<br> | |
| 125 | 125 | } |
| 126 | 126 | @ will no longer be shunned. But they may not exist in the repository. |
| 127 | 127 | @ It may be necessary to rebuild the repository using the |
| 128 | 128 | @ <b>fossil rebuild</b> command-line before the artifact content |
| 129 | 129 | @ can pulled in from other repositories.</p> |
| @@ -149,13 +149,13 @@ | ||
| 149 | 149 | db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); |
| 150 | 150 | } |
| 151 | 151 | admin_log("Shunned %Q", p); |
| 152 | 152 | p += strlen(p)+1; |
| 153 | 153 | } |
| 154 | - @ <p class="shunned">Artifact(s)<br /> | |
| 154 | + @ <p class="shunned">Artifact(s)<br> | |
| 155 | 155 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 156 | - @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> | |
| 156 | + @ <a href="%R/artifact/%s(p)">%s(p)</a><br> | |
| 157 | 157 | } |
| 158 | 158 | @ have been shunned. They will no longer be pushed. |
| 159 | 159 | @ They will be removed from the repository the next time the repository |
| 160 | 160 | @ is rebuilt using the <b>fossil rebuild</b> command-line</p> |
| 161 | 161 | } |
| @@ -200,11 +200,11 @@ | ||
| 200 | 200 | } |
| 201 | 201 | db_finalize(&q); |
| 202 | 202 | } |
| 203 | 203 | } |
| 204 | 204 | @ </textarea> |
| 205 | - @ <input type="submit" name="add" value="Shun" /> | |
| 205 | + @ <input type="submit" name="add" value="Shun"> | |
| 206 | 206 | @ </div></form> |
| 207 | 207 | @ </blockquote> |
| 208 | 208 | @ |
| 209 | 209 | @ <a name="delshun"></a> |
| 210 | 210 | @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be |
| @@ -227,11 +227,11 @@ | ||
| 227 | 227 | } |
| 228 | 228 | db_finalize(&q); |
| 229 | 229 | } |
| 230 | 230 | } |
| 231 | 231 | @ </textarea> |
| 232 | - @ <input type="submit" name="sub" value="Accept" /> | |
| 232 | + @ <input type="submit" name="sub" value="Accept"> | |
| 233 | 233 | @ </div></form> |
| 234 | 234 | @ </blockquote> |
| 235 | 235 | @ |
| 236 | 236 | @ <p>Press the Rebuild button below to rebuild the repository. The |
| 237 | 237 | @ content of newly shunned artifacts is not purged until the repository |
| @@ -239,27 +239,27 @@ | ||
| 239 | 239 | @ two, so be patient after pressing the button.</p> |
| 240 | 240 | @ |
| 241 | 241 | @ <blockquote> |
| 242 | 242 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 243 | 243 | login_insert_csrf_secret(); |
| 244 | - @ <input type="submit" name="rebuild" value="Rebuild" /> | |
| 244 | + @ <input type="submit" name="rebuild" value="Rebuild"> | |
| 245 | 245 | @ </div></form> |
| 246 | 246 | @ </blockquote> |
| 247 | 247 | @ |
| 248 | - @ <hr /><p>Shunned Artifacts:</p> | |
| 248 | + @ <hr><p>Shunned Artifacts:</p> | |
| 249 | 249 | @ <blockquote><p> |
| 250 | 250 | db_prepare(&q, |
| 251 | 251 | "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" |
| 252 | 252 | " FROM shun ORDER BY uuid"); |
| 253 | 253 | while( db_step(&q)==SQLITE_ROW ){ |
| 254 | 254 | const char *zUuid = db_column_text(&q, 0); |
| 255 | 255 | int stillExists = db_column_int(&q, 1); |
| 256 | 256 | cnt++; |
| 257 | 257 | if( stillExists ){ |
| 258 | - @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br /> | |
| 258 | + @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br> | |
| 259 | 259 | }else{ |
| 260 | - @ <b>%s(zUuid)</b><br /> | |
| 260 | + @ <b>%s(zUuid)</b><br> | |
| 261 | 261 | } |
| 262 | 262 | } |
| 263 | 263 | if( cnt==0 ){ |
| 264 | 264 | @ <i>no artifacts are shunned on this server</i> |
| 265 | 265 | } |
| @@ -481,11 +481,11 @@ | ||
| 481 | 481 | @ <tr><th valign="top" align="right">Artifacts:</th> |
| 482 | 482 | @ <td valign="top"> |
| 483 | 483 | } |
| 484 | 484 | cnt++; |
| 485 | 485 | @ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> |
| 486 | - @ %h(zDesc) (size: %d(size))<br /> | |
| 486 | + @ %h(zDesc) (size: %d(size))<br> | |
| 487 | 487 | } |
| 488 | 488 | if( cnt>0 ){ |
| 489 | 489 | @ <p> |
| 490 | 490 | if( db_exists( |
| 491 | 491 | "SELECT 1 FROM blob WHERE rcvid=%d AND" |
| @@ -531,13 +531,13 @@ | ||
| 531 | 531 | @ <tr><th valign="top" align="right">Unversioned Files:</th> |
| 532 | 532 | @ <td valign="top"> |
| 533 | 533 | } |
| 534 | 534 | cnt++; |
| 535 | 535 | if( isDeleted ){ |
| 536 | - @ %h(zName) (deleted)<br /> | |
| 536 | + @ %h(zName) (deleted)<br> | |
| 537 | 537 | }else{ |
| 538 | - @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br /> | |
| 538 | + @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br> | |
| 539 | 539 | } |
| 540 | 540 | } |
| 541 | 541 | if( cnt>0 ){ |
| 542 | 542 | @ <p><form action='%R/rcvfrom'> |
| 543 | 543 | @ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
| 544 | 544 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -111,19 +111,19 @@ | |
| 111 | } |
| 112 | admin_log("Unshunned %Q", p); |
| 113 | p += strlen(p)+1; |
| 114 | } |
| 115 | if( allExist ){ |
| 116 | @ <p class="noMoreShun">Artifact(s)<br /> |
| 117 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 118 | @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> |
| 119 | } |
| 120 | @ are no longer being shunned.</p> |
| 121 | }else{ |
| 122 | @ <p class="noMoreShun">Artifact(s)<br /> |
| 123 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 124 | @ %s(p)<br /> |
| 125 | } |
| 126 | @ will no longer be shunned. But they may not exist in the repository. |
| 127 | @ It may be necessary to rebuild the repository using the |
| 128 | @ <b>fossil rebuild</b> command-line before the artifact content |
| 129 | @ can pulled in from other repositories.</p> |
| @@ -149,13 +149,13 @@ | |
| 149 | db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); |
| 150 | } |
| 151 | admin_log("Shunned %Q", p); |
| 152 | p += strlen(p)+1; |
| 153 | } |
| 154 | @ <p class="shunned">Artifact(s)<br /> |
| 155 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 156 | @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> |
| 157 | } |
| 158 | @ have been shunned. They will no longer be pushed. |
| 159 | @ They will be removed from the repository the next time the repository |
| 160 | @ is rebuilt using the <b>fossil rebuild</b> command-line</p> |
| 161 | } |
| @@ -200,11 +200,11 @@ | |
| 200 | } |
| 201 | db_finalize(&q); |
| 202 | } |
| 203 | } |
| 204 | @ </textarea> |
| 205 | @ <input type="submit" name="add" value="Shun" /> |
| 206 | @ </div></form> |
| 207 | @ </blockquote> |
| 208 | @ |
| 209 | @ <a name="delshun"></a> |
| 210 | @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be |
| @@ -227,11 +227,11 @@ | |
| 227 | } |
| 228 | db_finalize(&q); |
| 229 | } |
| 230 | } |
| 231 | @ </textarea> |
| 232 | @ <input type="submit" name="sub" value="Accept" /> |
| 233 | @ </div></form> |
| 234 | @ </blockquote> |
| 235 | @ |
| 236 | @ <p>Press the Rebuild button below to rebuild the repository. The |
| 237 | @ content of newly shunned artifacts is not purged until the repository |
| @@ -239,27 +239,27 @@ | |
| 239 | @ two, so be patient after pressing the button.</p> |
| 240 | @ |
| 241 | @ <blockquote> |
| 242 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 243 | login_insert_csrf_secret(); |
| 244 | @ <input type="submit" name="rebuild" value="Rebuild" /> |
| 245 | @ </div></form> |
| 246 | @ </blockquote> |
| 247 | @ |
| 248 | @ <hr /><p>Shunned Artifacts:</p> |
| 249 | @ <blockquote><p> |
| 250 | db_prepare(&q, |
| 251 | "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" |
| 252 | " FROM shun ORDER BY uuid"); |
| 253 | while( db_step(&q)==SQLITE_ROW ){ |
| 254 | const char *zUuid = db_column_text(&q, 0); |
| 255 | int stillExists = db_column_int(&q, 1); |
| 256 | cnt++; |
| 257 | if( stillExists ){ |
| 258 | @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br /> |
| 259 | }else{ |
| 260 | @ <b>%s(zUuid)</b><br /> |
| 261 | } |
| 262 | } |
| 263 | if( cnt==0 ){ |
| 264 | @ <i>no artifacts are shunned on this server</i> |
| 265 | } |
| @@ -481,11 +481,11 @@ | |
| 481 | @ <tr><th valign="top" align="right">Artifacts:</th> |
| 482 | @ <td valign="top"> |
| 483 | } |
| 484 | cnt++; |
| 485 | @ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> |
| 486 | @ %h(zDesc) (size: %d(size))<br /> |
| 487 | } |
| 488 | if( cnt>0 ){ |
| 489 | @ <p> |
| 490 | if( db_exists( |
| 491 | "SELECT 1 FROM blob WHERE rcvid=%d AND" |
| @@ -531,13 +531,13 @@ | |
| 531 | @ <tr><th valign="top" align="right">Unversioned Files:</th> |
| 532 | @ <td valign="top"> |
| 533 | } |
| 534 | cnt++; |
| 535 | if( isDeleted ){ |
| 536 | @ %h(zName) (deleted)<br /> |
| 537 | }else{ |
| 538 | @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br /> |
| 539 | } |
| 540 | } |
| 541 | if( cnt>0 ){ |
| 542 | @ <p><form action='%R/rcvfrom'> |
| 543 | @ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
| 544 |
| --- src/shun.c | |
| +++ src/shun.c | |
| @@ -111,19 +111,19 @@ | |
| 111 | } |
| 112 | admin_log("Unshunned %Q", p); |
| 113 | p += strlen(p)+1; |
| 114 | } |
| 115 | if( allExist ){ |
| 116 | @ <p class="noMoreShun">Artifact(s)<br> |
| 117 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 118 | @ <a href="%R/artifact/%s(p)">%s(p)</a><br> |
| 119 | } |
| 120 | @ are no longer being shunned.</p> |
| 121 | }else{ |
| 122 | @ <p class="noMoreShun">Artifact(s)<br> |
| 123 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 124 | @ %s(p)<br> |
| 125 | } |
| 126 | @ will no longer be shunned. But they may not exist in the repository. |
| 127 | @ It may be necessary to rebuild the repository using the |
| 128 | @ <b>fossil rebuild</b> command-line before the artifact content |
| 129 | @ can pulled in from other repositories.</p> |
| @@ -149,13 +149,13 @@ | |
| 149 | db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); |
| 150 | } |
| 151 | admin_log("Shunned %Q", p); |
| 152 | p += strlen(p)+1; |
| 153 | } |
| 154 | @ <p class="shunned">Artifact(s)<br> |
| 155 | for( p = zUuid ; *p ; p += strlen(p)+1 ){ |
| 156 | @ <a href="%R/artifact/%s(p)">%s(p)</a><br> |
| 157 | } |
| 158 | @ have been shunned. They will no longer be pushed. |
| 159 | @ They will be removed from the repository the next time the repository |
| 160 | @ is rebuilt using the <b>fossil rebuild</b> command-line</p> |
| 161 | } |
| @@ -200,11 +200,11 @@ | |
| 200 | } |
| 201 | db_finalize(&q); |
| 202 | } |
| 203 | } |
| 204 | @ </textarea> |
| 205 | @ <input type="submit" name="add" value="Shun"> |
| 206 | @ </div></form> |
| 207 | @ </blockquote> |
| 208 | @ |
| 209 | @ <a name="delshun"></a> |
| 210 | @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be |
| @@ -227,11 +227,11 @@ | |
| 227 | } |
| 228 | db_finalize(&q); |
| 229 | } |
| 230 | } |
| 231 | @ </textarea> |
| 232 | @ <input type="submit" name="sub" value="Accept"> |
| 233 | @ </div></form> |
| 234 | @ </blockquote> |
| 235 | @ |
| 236 | @ <p>Press the Rebuild button below to rebuild the repository. The |
| 237 | @ content of newly shunned artifacts is not purged until the repository |
| @@ -239,27 +239,27 @@ | |
| 239 | @ two, so be patient after pressing the button.</p> |
| 240 | @ |
| 241 | @ <blockquote> |
| 242 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 243 | login_insert_csrf_secret(); |
| 244 | @ <input type="submit" name="rebuild" value="Rebuild"> |
| 245 | @ </div></form> |
| 246 | @ </blockquote> |
| 247 | @ |
| 248 | @ <hr><p>Shunned Artifacts:</p> |
| 249 | @ <blockquote><p> |
| 250 | db_prepare(&q, |
| 251 | "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" |
| 252 | " FROM shun ORDER BY uuid"); |
| 253 | while( db_step(&q)==SQLITE_ROW ){ |
| 254 | const char *zUuid = db_column_text(&q, 0); |
| 255 | int stillExists = db_column_int(&q, 1); |
| 256 | cnt++; |
| 257 | if( stillExists ){ |
| 258 | @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br> |
| 259 | }else{ |
| 260 | @ <b>%s(zUuid)</b><br> |
| 261 | } |
| 262 | } |
| 263 | if( cnt==0 ){ |
| 264 | @ <i>no artifacts are shunned on this server</i> |
| 265 | } |
| @@ -481,11 +481,11 @@ | |
| 481 | @ <tr><th valign="top" align="right">Artifacts:</th> |
| 482 | @ <td valign="top"> |
| 483 | } |
| 484 | cnt++; |
| 485 | @ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> |
| 486 | @ %h(zDesc) (size: %d(size))<br> |
| 487 | } |
| 488 | if( cnt>0 ){ |
| 489 | @ <p> |
| 490 | if( db_exists( |
| 491 | "SELECT 1 FROM blob WHERE rcvid=%d AND" |
| @@ -531,13 +531,13 @@ | |
| 531 | @ <tr><th valign="top" align="right">Unversioned Files:</th> |
| 532 | @ <td valign="top"> |
| 533 | } |
| 534 | cnt++; |
| 535 | if( isDeleted ){ |
| 536 | @ %h(zName) (deleted)<br> |
| 537 | }else{ |
| 538 | @ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br> |
| 539 | } |
| 540 | } |
| 541 | if( cnt>0 ){ |
| 542 | @ <p><form action='%R/rcvfrom'> |
| 543 | @ <input type="hidden" name="rcvid" value='%d(rcvid)'> |
| 544 |
+11
-11
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -538,13 +538,13 @@ | ||
| 538 | 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| 542 | 542 | @ be undone. Please confirm that this is what you want to do:</p> |
| 543 | - @ <input type="hidden" name="sn" value="%h(P("sn"))" /> | |
| 544 | - @ <input type="submit" name="del2" value="Confirm - Delete The Skin" /> | |
| 545 | - @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" /> | |
| 543 | + @ <input type="hidden" name="sn" value="%h(P("sn"))"> | |
| 544 | + @ <input type="submit" name="del2" value="Confirm - Delete The Skin"> | |
| 545 | + @ <input type="submit" name="cancel" value="Cancel - Do Not Delete"> | |
| 546 | 546 | login_insert_csrf_secret(); |
| 547 | 547 | @ </div></form> |
| 548 | 548 | style_finish_page(); |
| 549 | 549 | db_end_transaction(1); |
| 550 | 550 | return; |
| @@ -626,12 +626,12 @@ | ||
| 626 | 626 | if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ |
| 627 | 627 | @ (Currently In Use) |
| 628 | 628 | seenCurrent = 1; |
| 629 | 629 | }else{ |
| 630 | 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | - @ <input type="hidden" name="sn" value="%h(z)" /> | |
| 632 | - @ <input type="submit" name="load" value="Install" /> | |
| 631 | + @ <input type="hidden" name="sn" value="%h(z)"> | |
| 632 | + @ <input type="submit" name="load" value="Install"> | |
| 633 | 633 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 634 | 634 | @ (Current override) |
| 635 | 635 | } |
| 636 | 636 | @ </form> |
| 637 | 637 | } |
| @@ -858,22 +858,22 @@ | ||
| 858 | 858 | if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ |
| 859 | 859 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 860 | 860 | } |
| 861 | 861 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 862 | 862 | @ %h(zContent)</textarea> |
| 863 | - @ <br /> | |
| 864 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 863 | + @ <br> | |
| 864 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 865 | 865 | if( isRevert ){ |
| 866 | 866 | @ ← Press to complete reversion to "%s(zBasis)" |
| 867 | 867 | }else if( fossil_strcmp(zContent,zDflt)!=0 ){ |
| 868 | - @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"' /> | |
| 868 | + @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"'> | |
| 869 | 869 | } |
| 870 | - @ <hr /> | |
| 870 | + @ <hr> | |
| 871 | 871 | @ Baseline: \ |
| 872 | 872 | skin_emit_skin_selector("basis", zBasis, zDraft); |
| 873 | - @ <input type="submit" name="diff" value="Unified Diff" /> | |
| 874 | - @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff" /> | |
| 873 | + @ <input type="submit" name="diff" value="Unified Diff"> | |
| 874 | + @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff"> | |
| 875 | 875 | if( P("diff")!=0 || P("sbsdiff")!=0 ){ |
| 876 | 876 | Blob from, to, out; |
| 877 | 877 | DiffConfig DCfg; |
| 878 | 878 | construct_diff_flags(1, &DCfg); |
| 879 | 879 | DCfg.diffFlags |= DIFF_STRIP_EOLCR; |
| 880 | 880 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -538,13 +538,13 @@ | |
| 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| 542 | @ be undone. Please confirm that this is what you want to do:</p> |
| 543 | @ <input type="hidden" name="sn" value="%h(P("sn"))" /> |
| 544 | @ <input type="submit" name="del2" value="Confirm - Delete The Skin" /> |
| 545 | @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" /> |
| 546 | login_insert_csrf_secret(); |
| 547 | @ </div></form> |
| 548 | style_finish_page(); |
| 549 | db_end_transaction(1); |
| 550 | return; |
| @@ -626,12 +626,12 @@ | |
| 626 | if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ |
| 627 | @ (Currently In Use) |
| 628 | seenCurrent = 1; |
| 629 | }else{ |
| 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | @ <input type="hidden" name="sn" value="%h(z)" /> |
| 632 | @ <input type="submit" name="load" value="Install" /> |
| 633 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 634 | @ (Current override) |
| 635 | } |
| 636 | @ </form> |
| 637 | } |
| @@ -858,22 +858,22 @@ | |
| 858 | if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ |
| 859 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 860 | } |
| 861 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 862 | @ %h(zContent)</textarea> |
| 863 | @ <br /> |
| 864 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 865 | if( isRevert ){ |
| 866 | @ ← Press to complete reversion to "%s(zBasis)" |
| 867 | }else if( fossil_strcmp(zContent,zDflt)!=0 ){ |
| 868 | @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"' /> |
| 869 | } |
| 870 | @ <hr /> |
| 871 | @ Baseline: \ |
| 872 | skin_emit_skin_selector("basis", zBasis, zDraft); |
| 873 | @ <input type="submit" name="diff" value="Unified Diff" /> |
| 874 | @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff" /> |
| 875 | if( P("diff")!=0 || P("sbsdiff")!=0 ){ |
| 876 | Blob from, to, out; |
| 877 | DiffConfig DCfg; |
| 878 | construct_diff_flags(1, &DCfg); |
| 879 | DCfg.diffFlags |= DIFF_STRIP_EOLCR; |
| 880 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -538,13 +538,13 @@ | |
| 538 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 539 | style_header("Confirm Custom Skin Delete"); |
| 540 | @ <form action="%R/setup_skin_admin" method="post"><div> |
| 541 | @ <p>Deletion of a custom skin is a permanent action that cannot |
| 542 | @ be undone. Please confirm that this is what you want to do:</p> |
| 543 | @ <input type="hidden" name="sn" value="%h(P("sn"))"> |
| 544 | @ <input type="submit" name="del2" value="Confirm - Delete The Skin"> |
| 545 | @ <input type="submit" name="cancel" value="Cancel - Do Not Delete"> |
| 546 | login_insert_csrf_secret(); |
| 547 | @ </div></form> |
| 548 | style_finish_page(); |
| 549 | db_end_transaction(1); |
| 550 | return; |
| @@ -626,12 +626,12 @@ | |
| 626 | if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ |
| 627 | @ (Currently In Use) |
| 628 | seenCurrent = 1; |
| 629 | }else{ |
| 630 | @ <form action="%R/setup_skin_admin" method="post"> |
| 631 | @ <input type="hidden" name="sn" value="%h(z)"> |
| 632 | @ <input type="submit" name="load" value="Install"> |
| 633 | if( pAltSkin==&aBuiltinSkin[i] ){ |
| 634 | @ (Current override) |
| 635 | } |
| 636 | @ </form> |
| 637 | } |
| @@ -858,22 +858,22 @@ | |
| 858 | if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){ |
| 859 | db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile); |
| 860 | } |
| 861 | @ <textarea name="%s(zFile)" rows="10" cols="80">\ |
| 862 | @ %h(zContent)</textarea> |
| 863 | @ <br> |
| 864 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 865 | if( isRevert ){ |
| 866 | @ ← Press to complete reversion to "%s(zBasis)" |
| 867 | }else if( fossil_strcmp(zContent,zDflt)!=0 ){ |
| 868 | @ <input type="submit" name="revert" value='Revert To "%s(zBasis)"'> |
| 869 | } |
| 870 | @ <hr> |
| 871 | @ Baseline: \ |
| 872 | skin_emit_skin_selector("basis", zBasis, zDraft); |
| 873 | @ <input type="submit" name="diff" value="Unified Diff"> |
| 874 | @ <input type="submit" name="sbsdiff" value="Side-by-Side Diff"> |
| 875 | if( P("diff")!=0 || P("sbsdiff")!=0 ){ |
| 876 | Blob from, to, out; |
| 877 | DiffConfig DCfg; |
| 878 | construct_diff_flags(1, &DCfg); |
| 879 | DCfg.diffFlags |= DIFF_STRIP_EOLCR; |
| 880 |
+1
-1
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -287,11 +287,11 @@ | ||
| 287 | 287 | void fossil_key(const char **pzKey, int *pnKey){ |
| 288 | 288 | char *zSavedKey = db_get_saved_encryption_key(); |
| 289 | 289 | char *zKey; |
| 290 | 290 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 291 | 291 | |
| 292 | - if( zSavedKey==0 || savedKeySize==0 ) return; | |
| 292 | + if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return; | |
| 293 | 293 | zKey = (char*)malloc( savedKeySize ); |
| 294 | 294 | if( zKey ){ |
| 295 | 295 | memcpy(zKey, zSavedKey, savedKeySize); |
| 296 | 296 | *pzKey = zKey; |
| 297 | 297 | if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ |
| 298 | 298 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -287,11 +287,11 @@ | |
| 287 | void fossil_key(const char **pzKey, int *pnKey){ |
| 288 | char *zSavedKey = db_get_saved_encryption_key(); |
| 289 | char *zKey; |
| 290 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 291 | |
| 292 | if( zSavedKey==0 || savedKeySize==0 ) return; |
| 293 | zKey = (char*)malloc( savedKeySize ); |
| 294 | if( zKey ){ |
| 295 | memcpy(zKey, zSavedKey, savedKeySize); |
| 296 | *pzKey = zKey; |
| 297 | if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ |
| 298 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -287,11 +287,11 @@ | |
| 287 | void fossil_key(const char **pzKey, int *pnKey){ |
| 288 | char *zSavedKey = db_get_saved_encryption_key(); |
| 289 | char *zKey; |
| 290 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 291 | |
| 292 | if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return; |
| 293 | zKey = (char*)malloc( savedKeySize ); |
| 294 | if( zKey ){ |
| 295 | memcpy(zKey, zSavedKey, savedKeySize); |
| 296 | *pzKey = zKey; |
| 297 | if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ |
| 298 |
+146
-77
| --- src/statrep.c | ||
| +++ src/statrep.c | ||
| @@ -81,10 +81,20 @@ | ||
| 81 | 81 | case 'g': |
| 82 | 82 | case 'G': |
| 83 | 83 | zRealType = "g"; |
| 84 | 84 | rc = *zRealType; |
| 85 | 85 | break; |
| 86 | + case 'm': | |
| 87 | + case 'M': | |
| 88 | + zRealType = "m"; | |
| 89 | + rc = *zRealType; | |
| 90 | + break; | |
| 91 | + case 'n': | |
| 92 | + case 'N': | |
| 93 | + zRealType = "n"; | |
| 94 | + rc = *zRealType; | |
| 95 | + break; | |
| 86 | 96 | case 't': |
| 87 | 97 | case 'T': |
| 88 | 98 | zRealType = "t"; |
| 89 | 99 | rc = *zRealType; |
| 90 | 100 | break; |
| @@ -103,19 +113,28 @@ | ||
| 103 | 113 | " (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))", |
| 104 | 114 | P("from"), P("to")); |
| 105 | 115 | }else{ |
| 106 | 116 | zTimeSpan = " 1"; |
| 107 | 117 | } |
| 108 | - if(zRealType){ | |
| 118 | + if( zRealType==0 ){ | |
| 119 | + statsReportTimelineYFlag = "a"; | |
| 120 | + db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 121 | + "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/); | |
| 122 | + }else if( rc!='n' && rc!='m' ){ | |
| 109 | 123 | statsReportTimelineYFlag = zRealType; |
| 110 | 124 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 111 | 125 | "SELECT * FROM event WHERE (type GLOB %Q) AND %s", |
| 112 | 126 | zRealType, zTimeSpan/*safe-for-%s*/); |
| 113 | 127 | }else{ |
| 114 | - statsReportTimelineYFlag = "a"; | |
| 115 | - db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 116 | - "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/); | |
| 128 | + const char *zNot = rc=='n' ? "NOT" : ""; | |
| 129 | + statsReportTimelineYFlag = "ci"; | |
| 130 | + db_multi_exec( | |
| 131 | + "CREATE TEMP VIEW v_reports AS " | |
| 132 | + "SELECT * FROM event WHERE type='ci' AND %s" | |
| 133 | + " AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)", | |
| 134 | + zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/ | |
| 135 | + ); | |
| 117 | 136 | } |
| 118 | 137 | return statsReportType = rc; |
| 119 | 138 | } |
| 120 | 139 | |
| 121 | 140 | /* |
| @@ -127,10 +146,14 @@ | ||
| 127 | 146 | static const char *stats_report_label_for_type(){ |
| 128 | 147 | assert( statsReportType && "Must call stats_report_init_view() first." ); |
| 129 | 148 | switch( statsReportType ){ |
| 130 | 149 | case 'c': |
| 131 | 150 | return "check-ins"; |
| 151 | + case 'm': | |
| 152 | + return "merge check-ins"; | |
| 153 | + case 'n': | |
| 154 | + return "non-merge check-ins"; | |
| 132 | 155 | case 'e': |
| 133 | 156 | return "technotes"; |
| 134 | 157 | case 'f': |
| 135 | 158 | return "forum posts"; |
| 136 | 159 | case 'w': |
| @@ -143,48 +166,20 @@ | ||
| 143 | 166 | return "all types"; |
| 144 | 167 | } |
| 145 | 168 | } |
| 146 | 169 | |
| 147 | 170 | |
| 148 | -/* | |
| 149 | -** Helper for stats_report_by_month_year(), which generates a list of | |
| 150 | -** week numbers. zTimeframe should be either a timeframe in the form YYYY | |
| 151 | -** or YYYY-MM. | |
| 152 | -*/ | |
| 153 | -static void stats_report_output_week_links(const char *zTimeframe){ | |
| 154 | - Stmt stWeek = empty_Stmt; | |
| 155 | - char yearPart[5] = {0,0,0,0,0}; | |
| 156 | - memcpy(yearPart, zTimeframe, 4); | |
| 157 | - db_prepare(&stWeek, | |
| 158 | - "SELECT DISTINCT strftime('%%W',mtime) AS wk, " | |
| 159 | - "count(*) AS n, " | |
| 160 | - "substr(date(mtime),1,%d) AS ym " | |
| 161 | - "FROM v_reports " | |
| 162 | - "WHERE ym=%Q AND mtime < current_timestamp " | |
| 163 | - "GROUP BY wk ORDER BY wk", | |
| 164 | - strlen(zTimeframe), | |
| 165 | - zTimeframe); | |
| 166 | - while( SQLITE_ROW == db_step(&stWeek) ){ | |
| 167 | - const char *zWeek = db_column_text(&stWeek,0); | |
| 168 | - const int nCount = db_column_int(&stWeek,1); | |
| 169 | - cgi_printf("<a href='%R/timeline?" | |
| 170 | - "yw=%t-%t&n=%d&y=%s'>%s</a>", | |
| 171 | - yearPart, zWeek, | |
| 172 | - nCount, statsReportTimelineYFlag, zWeek); | |
| 173 | - } | |
| 174 | - db_finalize(&stWeek); | |
| 175 | -} | |
| 176 | - | |
| 177 | 171 | /* |
| 178 | 172 | ** Implements the "byyear" and "bymonth" reports for /reports. |
| 179 | 173 | ** If includeMonth is true then it generates the "bymonth" report, |
| 180 | 174 | ** else the "byyear" report. If zUserName is not NULL then the report is |
| 181 | 175 | ** restricted to events created by the named user account. |
| 182 | 176 | */ |
| 183 | -static void stats_report_by_month_year(char includeMonth, | |
| 184 | - char includeWeeks, | |
| 185 | - const char *zUserName){ | |
| 177 | +static void stats_report_by_month_year( | |
| 178 | + char includeMonth, /* 0 for stats-by-year. 1 for stats-by-month */ | |
| 179 | + const char *zUserName /* Only report events by this user */ | |
| 180 | +){ | |
| 186 | 181 | Stmt query = empty_Stmt; |
| 187 | 182 | int nRowNumber = 0; /* current TR number */ |
| 188 | 183 | int nEventTotal = 0; /* Total event count */ |
| 189 | 184 | int rowClass = 0; /* counter for alternating |
| 190 | 185 | row colors */ |
| @@ -197,32 +192,48 @@ | ||
| 197 | 192 | the per-year event totals */ |
| 198 | 193 | int nMaxEvents = 1; /* for calculating length of graph |
| 199 | 194 | bars. */ |
| 200 | 195 | int iterations = 0; /* number of weeks/months we iterate |
| 201 | 196 | over */ |
| 197 | + | |
| 198 | + char *zCurrentTF; /* The timeframe in which 'now' lives */ | |
| 199 | + double rNowFraction; /* Fraction of 'now' timeframe that has | |
| 200 | + passed */ | |
| 201 | + int nTFChar; /* Prefix of date() for timeframe */ | |
| 202 | + | |
| 203 | + nTFChar = includeMonth ? 7 : 4; | |
| 202 | 204 | stats_report_init_view(); |
| 203 | 205 | db_prepare(&query, |
| 204 | 206 | "SELECT substr(date(mtime),1,%d) AS timeframe," |
| 205 | 207 | " count(*) AS eventCount" |
| 206 | 208 | " FROM v_reports" |
| 207 | 209 | " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" |
| 208 | 210 | " GROUP BY timeframe" |
| 209 | 211 | " ORDER BY timeframe DESC", |
| 210 | - includeMonth ? 7 : 4, zUserName); | |
| 212 | + nTFChar, zUserName); | |
| 211 | 213 | @ <h1>Timeline Events (%s(stats_report_label_for_type())) |
| 212 | 214 | @ by year%s(includeMonth ? "/month" : "") |
| 213 | 215 | if( zUserName ){ |
| 214 | 216 | @ for user %h(zUserName) |
| 215 | 217 | } |
| 216 | 218 | @ </h1> |
| 217 | 219 | @ <table border='0' cellpadding='2' cellspacing='0' \ |
| 220 | + zCurrentTF = db_text(0, "SELECT substr(date(),1,%d)", nTFChar); | |
| 218 | 221 | if( !includeMonth ){ |
| 219 | 222 | @ class='statistics-report-table-events sortable' \ |
| 220 | 223 | @ data-column-types='tnx' data-init-sort='0'> |
| 221 | 224 | style_table_sorter(); |
| 225 | + rNowFraction = db_double(0.5, | |
| 226 | + "SELECT (unixepoch() - unixepoch('now','start of year'))*1.0/" | |
| 227 | + " (unixepoch('now','start of year','+1 year') - " | |
| 228 | + " unixepoch('now','start of year'));"); | |
| 222 | 229 | }else{ |
| 223 | 230 | @ class='statistics-report-table-events'> |
| 231 | + rNowFraction = db_double(0.5, | |
| 232 | + "SELECT (unixepoch() - unixepoch('now','start of month'))*1.0/" | |
| 233 | + " (unixepoch('now','start of month','+1 month') - " | |
| 234 | + " unixepoch('now','start of month'));"); | |
| 224 | 235 | } |
| 225 | 236 | @ <thead> |
| 226 | 237 | @ <th>%s(zTimeLabel)</th> |
| 227 | 238 | @ <th>Events</th> |
| 228 | 239 | @ <th width='90%%'><!-- relative commits graph --></th> |
| @@ -231,21 +242,26 @@ | ||
| 231 | 242 | Run the query twice. The first time we calculate the maximum |
| 232 | 243 | number of events for a given row. Maybe someone with better SQL |
| 233 | 244 | Fu can re-implement this with a single query. |
| 234 | 245 | */ |
| 235 | 246 | while( SQLITE_ROW == db_step(&query) ){ |
| 236 | - const int nCount = db_column_int(&query, 1); | |
| 247 | + int nCount = db_column_int(&query, 1); | |
| 248 | + if( strcmp(db_column_text(&query,0),zCurrentTF)==0 | |
| 249 | + && rNowFraction>0.05 | |
| 250 | + ){ | |
| 251 | + nCount = (int)(((double)nCount)/rNowFraction); | |
| 252 | + } | |
| 237 | 253 | if(nCount>nMaxEvents){ |
| 238 | 254 | nMaxEvents = nCount; |
| 239 | 255 | } |
| 240 | 256 | ++iterations; |
| 241 | 257 | } |
| 242 | 258 | db_reset(&query); |
| 243 | 259 | while( SQLITE_ROW == db_step(&query) ){ |
| 244 | 260 | const char *zTimeframe = db_column_text(&query, 0); |
| 245 | 261 | const int nCount = db_column_int(&query, 1); |
| 246 | - int nSize = nCount | |
| 262 | + int nSize = (nCount>0 && nMaxEvents>0) | |
| 247 | 263 | ? (int)(100 * nCount / nMaxEvents) |
| 248 | 264 | : 1; |
| 249 | 265 | showYearTotal = 0; |
| 250 | 266 | if(!nSize) nSize = 1; |
| 251 | 267 | if(includeMonth){ |
| @@ -294,25 +310,30 @@ | ||
| 294 | 310 | } |
| 295 | 311 | cgi_printf("'>%s</a>", zTimeframe); |
| 296 | 312 | } |
| 297 | 313 | @ </td><td>%d(nCount)</td> |
| 298 | 314 | @ <td> |
| 299 | - @ <div class='statistics-report-graph-line' | |
| 300 | - @ style='width:%d(nSize)%%;'> </div> | |
| 315 | + if( strcmp(zTimeframe, zCurrentTF)==0 | |
| 316 | + && rNowFraction>0.05 | |
| 317 | + && nCount>0 | |
| 318 | + && nMaxEvents>0 | |
| 319 | + ){ | |
| 320 | + /* If the timespan covered by this row contains "now", then project | |
| 321 | + ** the number of changes until the completion of the timespan and | |
| 322 | + ** show a dashed box of that projection. */ | |
| 323 | + int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; | |
| 324 | + int nXSize = (100 * nExtra)/nMaxEvents; | |
| 325 | + @ <span class='statistics-report-graph-line' \ | |
| 326 | + @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ | |
| 327 | + @ <span class='statistics-report-graph-extra' \ | |
| 328 | + @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ | |
| 329 | + }else{ | |
| 330 | + @ <div class='statistics-report-graph-line' \ | |
| 331 | + @ style='width:%d(nSize)%%;'> </div> \ | |
| 332 | + } | |
| 301 | 333 | @ </td> |
| 302 | - @</tr> | |
| 303 | - if(includeWeeks){ | |
| 304 | - /* This part works fine for months but it terribly slow (4.5s on my PC), | |
| 305 | - so it's only shown for by-year for now. Suggestions/patches for | |
| 306 | - a better/faster layout are welcomed. */ | |
| 307 | - @ <tr class='row%d(rowClass)'> | |
| 308 | - @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td> | |
| 309 | - @ <td class='statistics-report-week-of-year-list'> | |
| 310 | - stats_report_output_week_links(zTimeframe); | |
| 311 | - @ </td></tr> | |
| 312 | - } | |
| 313 | - | |
| 334 | + @ </tr> | |
| 314 | 335 | /* |
| 315 | 336 | Potential improvement: calculate the min/max event counts and |
| 316 | 337 | use percent-based graph bars. |
| 317 | 338 | */ |
| 318 | 339 | } |
| @@ -327,12 +348,12 @@ | ||
| 327 | 348 | } |
| 328 | 349 | @ </tbody></table> |
| 329 | 350 | if(nEventTotal){ |
| 330 | 351 | const char *zAvgLabel = includeMonth ? "month" : "year"; |
| 331 | 352 | int nAvg = iterations ? (nEventTotal/iterations) : 0; |
| 332 | - @ <br /><div>Total events: %d(nEventTotal) | |
| 333 | - @ <br />Average per active %s(zAvgLabel): %d(nAvg) | |
| 353 | + @ <br><div>Total events: %d(nEventTotal) | |
| 354 | + @ <br>Average per active %s(zAvgLabel): %d(nAvg) | |
| 334 | 355 | @ </div> |
| 335 | 356 | } |
| 336 | 357 | } |
| 337 | 358 | |
| 338 | 359 | /* |
| @@ -354,11 +375,11 @@ | ||
| 354 | 375 | " GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;" |
| 355 | 376 | ); |
| 356 | 377 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 357 | 378 | @ <center><svg width=700 height=400> |
| 358 | 379 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 359 | - @ </svg></centre><hr /> | |
| 380 | + @ </svg></centre><hr> | |
| 360 | 381 | } |
| 361 | 382 | style_table_sorter(); |
| 362 | 383 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 363 | 384 | @ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'> |
| 364 | 385 | @ <thead><tr> |
| @@ -510,11 +531,11 @@ | ||
| 510 | 531 | , zUserName |
| 511 | 532 | ); |
| 512 | 533 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 513 | 534 | @ <center><svg width=700 height=400> |
| 514 | 535 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 515 | - @ </svg></centre><hr /> | |
| 536 | + @ </svg></centre><hr> | |
| 516 | 537 | } |
| 517 | 538 | style_table_sorter(); |
| 518 | 539 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 519 | 540 | @ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'> |
| 520 | 541 | @ <thead><tr> |
| @@ -587,11 +608,11 @@ | ||
| 587 | 608 | zUserName |
| 588 | 609 | ); |
| 589 | 610 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 590 | 611 | @ <center><svg width=700 height=400> |
| 591 | 612 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 592 | - @ </svg></centre><hr /> | |
| 613 | + @ </svg></centre><hr> | |
| 593 | 614 | } |
| 594 | 615 | style_table_sorter(); |
| 595 | 616 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 596 | 617 | @ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'> |
| 597 | 618 | @ <thead><tr> |
| @@ -629,12 +650,12 @@ | ||
| 629 | 650 | } |
| 630 | 651 | |
| 631 | 652 | |
| 632 | 653 | /* |
| 633 | 654 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 634 | -** week numbers. zTimeframe should be either a timeframe in the form YYYY | |
| 635 | -** or YYYY-MM. If zUserName is not NULL then the report is restricted to events | |
| 655 | +** week numbers. The "y" query parameter is the year in format YYYY. | |
| 656 | +** If zUserName is not NULL then the report is restricted to events | |
| 636 | 657 | ** created by the named user account. |
| 637 | 658 | */ |
| 638 | 659 | static void stats_report_year_weeks(const char *zUserName){ |
| 639 | 660 | const char *zYear = P("y"); /* Year for which report shown */ |
| 640 | 661 | Stmt q; |
| @@ -641,10 +662,13 @@ | ||
| 641 | 662 | int nMaxEvents = 1; /* max number of events for |
| 642 | 663 | all rows. */ |
| 643 | 664 | int iterations = 0; /* # of active time periods. */ |
| 644 | 665 | int rowCount = 0; |
| 645 | 666 | int total = 0; |
| 667 | + char *zCurrentWeek; /* Current week number */ | |
| 668 | + double rNowFraction = 0.0; /* Fraction of current week that has | |
| 669 | + ** passed */ | |
| 646 | 670 | |
| 647 | 671 | stats_report_init_view(); |
| 648 | 672 | style_submenu_sql("y", "Year:", |
| 649 | 673 | "WITH RECURSIVE a(b) AS (" |
| 650 | 674 | " SELECT substr(date('now'),1,4) UNION ALL" |
| @@ -652,11 +676,11 @@ | ||
| 652 | 676 | " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)" |
| 653 | 677 | ") SELECT b, b FROM a ORDER BY b DESC"); |
| 654 | 678 | if( zYear==0 || strlen(zYear)!=4 ){ |
| 655 | 679 | zYear = db_text("1970","SELECT substr(date('now'),1,4);"); |
| 656 | 680 | } |
| 657 | - cgi_printf("<br />"); | |
| 681 | + cgi_printf("<br>\n"); | |
| 658 | 682 | db_prepare(&q, |
| 659 | 683 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 660 | 684 | " count(*) AS n " |
| 661 | 685 | " FROM v_reports " |
| 662 | 686 | " WHERE %Q=substr(date(mtime),1,4) " |
| @@ -667,32 +691,45 @@ | ||
| 667 | 691 | @ for the calendar weeks of %h(zYear) |
| 668 | 692 | if( zUserName ){ |
| 669 | 693 | @ for user %h(zUserName) |
| 670 | 694 | } |
| 671 | 695 | @ </h1> |
| 672 | - style_table_sorter(); | |
| 696 | + zCurrentWeek = db_text(0, | |
| 697 | + "SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'", | |
| 698 | + zYear); | |
| 699 | + if( zCurrentWeek ){ | |
| 700 | + rNowFraction = db_double(0.5, | |
| 701 | + "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;"); | |
| 702 | + } | |
| 703 | + style_table_sorter(); | |
| 673 | 704 | cgi_printf("<table class='statistics-report-table-events sortable' " |
| 674 | 705 | "border='0' cellpadding='2' width='100%%' " |
| 675 | - "cellspacing='0' data-column-types='tnx' data-init-sort='0'>"); | |
| 706 | + "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n"); | |
| 676 | 707 | cgi_printf("<thead><tr>" |
| 677 | 708 | "<th>Week</th>" |
| 678 | 709 | "<th>Events</th>" |
| 679 | 710 | "<th width='90%%'><!-- relative commits graph --></th>" |
| 680 | - "</tr></thead>" | |
| 681 | - "<tbody>"); | |
| 711 | + "</tr></thead>\n" | |
| 712 | + "<tbody>\n"); | |
| 682 | 713 | while( SQLITE_ROW == db_step(&q) ){ |
| 683 | - const int nCount = db_column_int(&q, 1); | |
| 714 | + int nCount = db_column_int(&q, 1); | |
| 715 | + if( zCurrentWeek!=0 | |
| 716 | + && strcmp(db_column_text(&q,0),zCurrentWeek)==0 | |
| 717 | + && rNowFraction>0.05 | |
| 718 | + ){ | |
| 719 | + nCount = (int)(((double)nCount)/rNowFraction); | |
| 720 | + } | |
| 684 | 721 | if(nCount>nMaxEvents){ |
| 685 | 722 | nMaxEvents = nCount; |
| 686 | 723 | } |
| 687 | 724 | ++iterations; |
| 688 | 725 | } |
| 689 | 726 | db_reset(&q); |
| 690 | 727 | while( SQLITE_ROW == db_step(&q) ){ |
| 691 | 728 | const char *zWeek = db_column_text(&q,0); |
| 692 | 729 | const int nCount = db_column_int(&q,1); |
| 693 | - int nSize = nCount | |
| 730 | + int nSize = (nCount>0 && nMaxEvents>0) | |
| 694 | 731 | ? (int)(100 * nCount / nMaxEvents) |
| 695 | 732 | : 0; |
| 696 | 733 | if(!nSize) nSize = 1; |
| 697 | 734 | total += nCount; |
| 698 | 735 | cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); |
| @@ -704,22 +741,37 @@ | ||
| 704 | 741 | } |
| 705 | 742 | cgi_printf("'>%s</a></td>",zWeek); |
| 706 | 743 | |
| 707 | 744 | cgi_printf("<td>%d</td>",nCount); |
| 708 | 745 | cgi_printf("<td>"); |
| 709 | - if(nCount){ | |
| 710 | - cgi_printf("<div class='statistics-report-graph-line'" | |
| 711 | - "style='width:%d%%;'> </div>", | |
| 712 | - nSize); | |
| 746 | + if( nCount ){ | |
| 747 | + if( zCurrentWeek!=0 | |
| 748 | + && strcmp(zWeek, zCurrentWeek)==0 | |
| 749 | + && rNowFraction>0.05 | |
| 750 | + && nMaxEvents>0 | |
| 751 | + ){ | |
| 752 | + /* If the covered covered by this row contains "now", then project | |
| 753 | + ** the number of changes until the completion of the week and | |
| 754 | + ** show a dashed box of that projection. */ | |
| 755 | + int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; | |
| 756 | + int nXSize = (100 * nExtra)/nMaxEvents; | |
| 757 | + @ <span class='statistics-report-graph-line' \ | |
| 758 | + @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ | |
| 759 | + @ <span class='statistics-report-graph-extra' \ | |
| 760 | + @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ | |
| 761 | + }else{ | |
| 762 | + @ <div class='statistics-report-graph-line' \ | |
| 763 | + @ style='width:%d(nSize)%%;'> </div> \ | |
| 764 | + } | |
| 713 | 765 | } |
| 714 | - cgi_printf("</td></tr>"); | |
| 766 | + cgi_printf("</td></tr>\n"); | |
| 715 | 767 | } |
| 716 | 768 | db_finalize(&q); |
| 717 | 769 | cgi_printf("</tbody></table>"); |
| 718 | 770 | if(total){ |
| 719 | 771 | int nAvg = iterations ? (total/iterations) : 0; |
| 720 | - cgi_printf("<br /><div>Total events: %d<br />" | |
| 772 | + cgi_printf("<br><div>Total events: %d<br>" | |
| 721 | 773 | "Average per active week: %d</div>", |
| 722 | 774 | total, nAvg); |
| 723 | 775 | } |
| 724 | 776 | } |
| 725 | 777 | |
| @@ -785,14 +837,29 @@ | ||
| 785 | 837 | ** |
| 786 | 838 | ** Shows activity reports for the repository. |
| 787 | 839 | ** |
| 788 | 840 | ** Query Parameters: |
| 789 | 841 | ** |
| 790 | -** view=REPORT_NAME Valid values: bymonth, byyear, byuser | |
| 842 | +** view=REPORT_NAME Valid REPORT_NAME values: | |
| 843 | +** * byyear | |
| 844 | +** * bymonth | |
| 845 | +** * byweek | |
| 846 | +** * byweekday | |
| 847 | +** * byhour | |
| 848 | +** * byuser | |
| 849 | +** * byfile | |
| 850 | +** * lastchng | |
| 791 | 851 | ** user=NAME Restricts statistics to the given user |
| 792 | 852 | ** type=TYPE Restricts the report to a specific event type: |
| 793 | -** ci (check-in), f (forum), w (wiki), t (ticket), g (tag) | |
| 853 | +** * all (everything), | |
| 854 | +** * ci (check-in) | |
| 855 | +** * m (merge check-in), | |
| 856 | +** * n (non-merge check-in) | |
| 857 | +** * f (forum post) | |
| 858 | +** * w (wiki page change) | |
| 859 | +** * t (ticket change) | |
| 860 | +** * g (tag added or removed) | |
| 794 | 861 | ** Defaulting to all event types. |
| 795 | 862 | ** |
| 796 | 863 | ** The view-specific query parameters include: |
| 797 | 864 | ** |
| 798 | 865 | ** view=byweek: |
| @@ -822,10 +889,12 @@ | ||
| 822 | 889 | }; |
| 823 | 890 | static const char *const azType[] = { |
| 824 | 891 | "a", "All Changes", |
| 825 | 892 | "ci", "Check-ins", |
| 826 | 893 | "f", "Forum Posts", |
| 894 | + "m", "Merge check-ins", | |
| 895 | + "n", "Non-merge check-ins", | |
| 827 | 896 | "g", "Tags", |
| 828 | 897 | "e", "Tech Notes", |
| 829 | 898 | "t", "Tickets", |
| 830 | 899 | "w", "Wiki" |
| 831 | 900 | }; |
| @@ -867,14 +936,14 @@ | ||
| 867 | 936 | } |
| 868 | 937 | style_submenu_element("Stats", "%R/stat"); |
| 869 | 938 | style_header("Activity Reports"); |
| 870 | 939 | switch( eType ){ |
| 871 | 940 | case RPT_BYYEAR: |
| 872 | - stats_report_by_month_year(0, 0, zUserName); | |
| 941 | + stats_report_by_month_year(0, zUserName); | |
| 873 | 942 | break; |
| 874 | 943 | case RPT_BYMONTH: |
| 875 | - stats_report_by_month_year(1, 0, zUserName); | |
| 944 | + stats_report_by_month_year(1, zUserName); | |
| 876 | 945 | break; |
| 877 | 946 | case RPT_BYWEEK: |
| 878 | 947 | stats_report_year_weeks(zUserName); |
| 879 | 948 | break; |
| 880 | 949 | default: |
| 881 | 950 |
| --- src/statrep.c | |
| +++ src/statrep.c | |
| @@ -81,10 +81,20 @@ | |
| 81 | case 'g': |
| 82 | case 'G': |
| 83 | zRealType = "g"; |
| 84 | rc = *zRealType; |
| 85 | break; |
| 86 | case 't': |
| 87 | case 'T': |
| 88 | zRealType = "t"; |
| 89 | rc = *zRealType; |
| 90 | break; |
| @@ -103,19 +113,28 @@ | |
| 103 | " (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))", |
| 104 | P("from"), P("to")); |
| 105 | }else{ |
| 106 | zTimeSpan = " 1"; |
| 107 | } |
| 108 | if(zRealType){ |
| 109 | statsReportTimelineYFlag = zRealType; |
| 110 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 111 | "SELECT * FROM event WHERE (type GLOB %Q) AND %s", |
| 112 | zRealType, zTimeSpan/*safe-for-%s*/); |
| 113 | }else{ |
| 114 | statsReportTimelineYFlag = "a"; |
| 115 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 116 | "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/); |
| 117 | } |
| 118 | return statsReportType = rc; |
| 119 | } |
| 120 | |
| 121 | /* |
| @@ -127,10 +146,14 @@ | |
| 127 | static const char *stats_report_label_for_type(){ |
| 128 | assert( statsReportType && "Must call stats_report_init_view() first." ); |
| 129 | switch( statsReportType ){ |
| 130 | case 'c': |
| 131 | return "check-ins"; |
| 132 | case 'e': |
| 133 | return "technotes"; |
| 134 | case 'f': |
| 135 | return "forum posts"; |
| 136 | case 'w': |
| @@ -143,48 +166,20 @@ | |
| 143 | return "all types"; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | |
| 148 | /* |
| 149 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 150 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 151 | ** or YYYY-MM. |
| 152 | */ |
| 153 | static void stats_report_output_week_links(const char *zTimeframe){ |
| 154 | Stmt stWeek = empty_Stmt; |
| 155 | char yearPart[5] = {0,0,0,0,0}; |
| 156 | memcpy(yearPart, zTimeframe, 4); |
| 157 | db_prepare(&stWeek, |
| 158 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 159 | "count(*) AS n, " |
| 160 | "substr(date(mtime),1,%d) AS ym " |
| 161 | "FROM v_reports " |
| 162 | "WHERE ym=%Q AND mtime < current_timestamp " |
| 163 | "GROUP BY wk ORDER BY wk", |
| 164 | strlen(zTimeframe), |
| 165 | zTimeframe); |
| 166 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| 167 | const char *zWeek = db_column_text(&stWeek,0); |
| 168 | const int nCount = db_column_int(&stWeek,1); |
| 169 | cgi_printf("<a href='%R/timeline?" |
| 170 | "yw=%t-%t&n=%d&y=%s'>%s</a>", |
| 171 | yearPart, zWeek, |
| 172 | nCount, statsReportTimelineYFlag, zWeek); |
| 173 | } |
| 174 | db_finalize(&stWeek); |
| 175 | } |
| 176 | |
| 177 | /* |
| 178 | ** Implements the "byyear" and "bymonth" reports for /reports. |
| 179 | ** If includeMonth is true then it generates the "bymonth" report, |
| 180 | ** else the "byyear" report. If zUserName is not NULL then the report is |
| 181 | ** restricted to events created by the named user account. |
| 182 | */ |
| 183 | static void stats_report_by_month_year(char includeMonth, |
| 184 | char includeWeeks, |
| 185 | const char *zUserName){ |
| 186 | Stmt query = empty_Stmt; |
| 187 | int nRowNumber = 0; /* current TR number */ |
| 188 | int nEventTotal = 0; /* Total event count */ |
| 189 | int rowClass = 0; /* counter for alternating |
| 190 | row colors */ |
| @@ -197,32 +192,48 @@ | |
| 197 | the per-year event totals */ |
| 198 | int nMaxEvents = 1; /* for calculating length of graph |
| 199 | bars. */ |
| 200 | int iterations = 0; /* number of weeks/months we iterate |
| 201 | over */ |
| 202 | stats_report_init_view(); |
| 203 | db_prepare(&query, |
| 204 | "SELECT substr(date(mtime),1,%d) AS timeframe," |
| 205 | " count(*) AS eventCount" |
| 206 | " FROM v_reports" |
| 207 | " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" |
| 208 | " GROUP BY timeframe" |
| 209 | " ORDER BY timeframe DESC", |
| 210 | includeMonth ? 7 : 4, zUserName); |
| 211 | @ <h1>Timeline Events (%s(stats_report_label_for_type())) |
| 212 | @ by year%s(includeMonth ? "/month" : "") |
| 213 | if( zUserName ){ |
| 214 | @ for user %h(zUserName) |
| 215 | } |
| 216 | @ </h1> |
| 217 | @ <table border='0' cellpadding='2' cellspacing='0' \ |
| 218 | if( !includeMonth ){ |
| 219 | @ class='statistics-report-table-events sortable' \ |
| 220 | @ data-column-types='tnx' data-init-sort='0'> |
| 221 | style_table_sorter(); |
| 222 | }else{ |
| 223 | @ class='statistics-report-table-events'> |
| 224 | } |
| 225 | @ <thead> |
| 226 | @ <th>%s(zTimeLabel)</th> |
| 227 | @ <th>Events</th> |
| 228 | @ <th width='90%%'><!-- relative commits graph --></th> |
| @@ -231,21 +242,26 @@ | |
| 231 | Run the query twice. The first time we calculate the maximum |
| 232 | number of events for a given row. Maybe someone with better SQL |
| 233 | Fu can re-implement this with a single query. |
| 234 | */ |
| 235 | while( SQLITE_ROW == db_step(&query) ){ |
| 236 | const int nCount = db_column_int(&query, 1); |
| 237 | if(nCount>nMaxEvents){ |
| 238 | nMaxEvents = nCount; |
| 239 | } |
| 240 | ++iterations; |
| 241 | } |
| 242 | db_reset(&query); |
| 243 | while( SQLITE_ROW == db_step(&query) ){ |
| 244 | const char *zTimeframe = db_column_text(&query, 0); |
| 245 | const int nCount = db_column_int(&query, 1); |
| 246 | int nSize = nCount |
| 247 | ? (int)(100 * nCount / nMaxEvents) |
| 248 | : 1; |
| 249 | showYearTotal = 0; |
| 250 | if(!nSize) nSize = 1; |
| 251 | if(includeMonth){ |
| @@ -294,25 +310,30 @@ | |
| 294 | } |
| 295 | cgi_printf("'>%s</a>", zTimeframe); |
| 296 | } |
| 297 | @ </td><td>%d(nCount)</td> |
| 298 | @ <td> |
| 299 | @ <div class='statistics-report-graph-line' |
| 300 | @ style='width:%d(nSize)%%;'> </div> |
| 301 | @ </td> |
| 302 | @</tr> |
| 303 | if(includeWeeks){ |
| 304 | /* This part works fine for months but it terribly slow (4.5s on my PC), |
| 305 | so it's only shown for by-year for now. Suggestions/patches for |
| 306 | a better/faster layout are welcomed. */ |
| 307 | @ <tr class='row%d(rowClass)'> |
| 308 | @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td> |
| 309 | @ <td class='statistics-report-week-of-year-list'> |
| 310 | stats_report_output_week_links(zTimeframe); |
| 311 | @ </td></tr> |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | Potential improvement: calculate the min/max event counts and |
| 316 | use percent-based graph bars. |
| 317 | */ |
| 318 | } |
| @@ -327,12 +348,12 @@ | |
| 327 | } |
| 328 | @ </tbody></table> |
| 329 | if(nEventTotal){ |
| 330 | const char *zAvgLabel = includeMonth ? "month" : "year"; |
| 331 | int nAvg = iterations ? (nEventTotal/iterations) : 0; |
| 332 | @ <br /><div>Total events: %d(nEventTotal) |
| 333 | @ <br />Average per active %s(zAvgLabel): %d(nAvg) |
| 334 | @ </div> |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | /* |
| @@ -354,11 +375,11 @@ | |
| 354 | " GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;" |
| 355 | ); |
| 356 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 357 | @ <center><svg width=700 height=400> |
| 358 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 359 | @ </svg></centre><hr /> |
| 360 | } |
| 361 | style_table_sorter(); |
| 362 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 363 | @ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'> |
| 364 | @ <thead><tr> |
| @@ -510,11 +531,11 @@ | |
| 510 | , zUserName |
| 511 | ); |
| 512 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 513 | @ <center><svg width=700 height=400> |
| 514 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 515 | @ </svg></centre><hr /> |
| 516 | } |
| 517 | style_table_sorter(); |
| 518 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 519 | @ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'> |
| 520 | @ <thead><tr> |
| @@ -587,11 +608,11 @@ | |
| 587 | zUserName |
| 588 | ); |
| 589 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 590 | @ <center><svg width=700 height=400> |
| 591 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 592 | @ </svg></centre><hr /> |
| 593 | } |
| 594 | style_table_sorter(); |
| 595 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 596 | @ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'> |
| 597 | @ <thead><tr> |
| @@ -629,12 +650,12 @@ | |
| 629 | } |
| 630 | |
| 631 | |
| 632 | /* |
| 633 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 634 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 635 | ** or YYYY-MM. If zUserName is not NULL then the report is restricted to events |
| 636 | ** created by the named user account. |
| 637 | */ |
| 638 | static void stats_report_year_weeks(const char *zUserName){ |
| 639 | const char *zYear = P("y"); /* Year for which report shown */ |
| 640 | Stmt q; |
| @@ -641,10 +662,13 @@ | |
| 641 | int nMaxEvents = 1; /* max number of events for |
| 642 | all rows. */ |
| 643 | int iterations = 0; /* # of active time periods. */ |
| 644 | int rowCount = 0; |
| 645 | int total = 0; |
| 646 | |
| 647 | stats_report_init_view(); |
| 648 | style_submenu_sql("y", "Year:", |
| 649 | "WITH RECURSIVE a(b) AS (" |
| 650 | " SELECT substr(date('now'),1,4) UNION ALL" |
| @@ -652,11 +676,11 @@ | |
| 652 | " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)" |
| 653 | ") SELECT b, b FROM a ORDER BY b DESC"); |
| 654 | if( zYear==0 || strlen(zYear)!=4 ){ |
| 655 | zYear = db_text("1970","SELECT substr(date('now'),1,4);"); |
| 656 | } |
| 657 | cgi_printf("<br />"); |
| 658 | db_prepare(&q, |
| 659 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 660 | " count(*) AS n " |
| 661 | " FROM v_reports " |
| 662 | " WHERE %Q=substr(date(mtime),1,4) " |
| @@ -667,32 +691,45 @@ | |
| 667 | @ for the calendar weeks of %h(zYear) |
| 668 | if( zUserName ){ |
| 669 | @ for user %h(zUserName) |
| 670 | } |
| 671 | @ </h1> |
| 672 | style_table_sorter(); |
| 673 | cgi_printf("<table class='statistics-report-table-events sortable' " |
| 674 | "border='0' cellpadding='2' width='100%%' " |
| 675 | "cellspacing='0' data-column-types='tnx' data-init-sort='0'>"); |
| 676 | cgi_printf("<thead><tr>" |
| 677 | "<th>Week</th>" |
| 678 | "<th>Events</th>" |
| 679 | "<th width='90%%'><!-- relative commits graph --></th>" |
| 680 | "</tr></thead>" |
| 681 | "<tbody>"); |
| 682 | while( SQLITE_ROW == db_step(&q) ){ |
| 683 | const int nCount = db_column_int(&q, 1); |
| 684 | if(nCount>nMaxEvents){ |
| 685 | nMaxEvents = nCount; |
| 686 | } |
| 687 | ++iterations; |
| 688 | } |
| 689 | db_reset(&q); |
| 690 | while( SQLITE_ROW == db_step(&q) ){ |
| 691 | const char *zWeek = db_column_text(&q,0); |
| 692 | const int nCount = db_column_int(&q,1); |
| 693 | int nSize = nCount |
| 694 | ? (int)(100 * nCount / nMaxEvents) |
| 695 | : 0; |
| 696 | if(!nSize) nSize = 1; |
| 697 | total += nCount; |
| 698 | cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); |
| @@ -704,22 +741,37 @@ | |
| 704 | } |
| 705 | cgi_printf("'>%s</a></td>",zWeek); |
| 706 | |
| 707 | cgi_printf("<td>%d</td>",nCount); |
| 708 | cgi_printf("<td>"); |
| 709 | if(nCount){ |
| 710 | cgi_printf("<div class='statistics-report-graph-line'" |
| 711 | "style='width:%d%%;'> </div>", |
| 712 | nSize); |
| 713 | } |
| 714 | cgi_printf("</td></tr>"); |
| 715 | } |
| 716 | db_finalize(&q); |
| 717 | cgi_printf("</tbody></table>"); |
| 718 | if(total){ |
| 719 | int nAvg = iterations ? (total/iterations) : 0; |
| 720 | cgi_printf("<br /><div>Total events: %d<br />" |
| 721 | "Average per active week: %d</div>", |
| 722 | total, nAvg); |
| 723 | } |
| 724 | } |
| 725 | |
| @@ -785,14 +837,29 @@ | |
| 785 | ** |
| 786 | ** Shows activity reports for the repository. |
| 787 | ** |
| 788 | ** Query Parameters: |
| 789 | ** |
| 790 | ** view=REPORT_NAME Valid values: bymonth, byyear, byuser |
| 791 | ** user=NAME Restricts statistics to the given user |
| 792 | ** type=TYPE Restricts the report to a specific event type: |
| 793 | ** ci (check-in), f (forum), w (wiki), t (ticket), g (tag) |
| 794 | ** Defaulting to all event types. |
| 795 | ** |
| 796 | ** The view-specific query parameters include: |
| 797 | ** |
| 798 | ** view=byweek: |
| @@ -822,10 +889,12 @@ | |
| 822 | }; |
| 823 | static const char *const azType[] = { |
| 824 | "a", "All Changes", |
| 825 | "ci", "Check-ins", |
| 826 | "f", "Forum Posts", |
| 827 | "g", "Tags", |
| 828 | "e", "Tech Notes", |
| 829 | "t", "Tickets", |
| 830 | "w", "Wiki" |
| 831 | }; |
| @@ -867,14 +936,14 @@ | |
| 867 | } |
| 868 | style_submenu_element("Stats", "%R/stat"); |
| 869 | style_header("Activity Reports"); |
| 870 | switch( eType ){ |
| 871 | case RPT_BYYEAR: |
| 872 | stats_report_by_month_year(0, 0, zUserName); |
| 873 | break; |
| 874 | case RPT_BYMONTH: |
| 875 | stats_report_by_month_year(1, 0, zUserName); |
| 876 | break; |
| 877 | case RPT_BYWEEK: |
| 878 | stats_report_year_weeks(zUserName); |
| 879 | break; |
| 880 | default: |
| 881 |
| --- src/statrep.c | |
| +++ src/statrep.c | |
| @@ -81,10 +81,20 @@ | |
| 81 | case 'g': |
| 82 | case 'G': |
| 83 | zRealType = "g"; |
| 84 | rc = *zRealType; |
| 85 | break; |
| 86 | case 'm': |
| 87 | case 'M': |
| 88 | zRealType = "m"; |
| 89 | rc = *zRealType; |
| 90 | break; |
| 91 | case 'n': |
| 92 | case 'N': |
| 93 | zRealType = "n"; |
| 94 | rc = *zRealType; |
| 95 | break; |
| 96 | case 't': |
| 97 | case 'T': |
| 98 | zRealType = "t"; |
| 99 | rc = *zRealType; |
| 100 | break; |
| @@ -103,19 +113,28 @@ | |
| 113 | " (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))", |
| 114 | P("from"), P("to")); |
| 115 | }else{ |
| 116 | zTimeSpan = " 1"; |
| 117 | } |
| 118 | if( zRealType==0 ){ |
| 119 | statsReportTimelineYFlag = "a"; |
| 120 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 121 | "SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/); |
| 122 | }else if( rc!='n' && rc!='m' ){ |
| 123 | statsReportTimelineYFlag = zRealType; |
| 124 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 125 | "SELECT * FROM event WHERE (type GLOB %Q) AND %s", |
| 126 | zRealType, zTimeSpan/*safe-for-%s*/); |
| 127 | }else{ |
| 128 | const char *zNot = rc=='n' ? "NOT" : ""; |
| 129 | statsReportTimelineYFlag = "ci"; |
| 130 | db_multi_exec( |
| 131 | "CREATE TEMP VIEW v_reports AS " |
| 132 | "SELECT * FROM event WHERE type='ci' AND %s" |
| 133 | " AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)", |
| 134 | zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/ |
| 135 | ); |
| 136 | } |
| 137 | return statsReportType = rc; |
| 138 | } |
| 139 | |
| 140 | /* |
| @@ -127,10 +146,14 @@ | |
| 146 | static const char *stats_report_label_for_type(){ |
| 147 | assert( statsReportType && "Must call stats_report_init_view() first." ); |
| 148 | switch( statsReportType ){ |
| 149 | case 'c': |
| 150 | return "check-ins"; |
| 151 | case 'm': |
| 152 | return "merge check-ins"; |
| 153 | case 'n': |
| 154 | return "non-merge check-ins"; |
| 155 | case 'e': |
| 156 | return "technotes"; |
| 157 | case 'f': |
| 158 | return "forum posts"; |
| 159 | case 'w': |
| @@ -143,48 +166,20 @@ | |
| 166 | return "all types"; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | |
| 171 | /* |
| 172 | ** Implements the "byyear" and "bymonth" reports for /reports. |
| 173 | ** If includeMonth is true then it generates the "bymonth" report, |
| 174 | ** else the "byyear" report. If zUserName is not NULL then the report is |
| 175 | ** restricted to events created by the named user account. |
| 176 | */ |
| 177 | static void stats_report_by_month_year( |
| 178 | char includeMonth, /* 0 for stats-by-year. 1 for stats-by-month */ |
| 179 | const char *zUserName /* Only report events by this user */ |
| 180 | ){ |
| 181 | Stmt query = empty_Stmt; |
| 182 | int nRowNumber = 0; /* current TR number */ |
| 183 | int nEventTotal = 0; /* Total event count */ |
| 184 | int rowClass = 0; /* counter for alternating |
| 185 | row colors */ |
| @@ -197,32 +192,48 @@ | |
| 192 | the per-year event totals */ |
| 193 | int nMaxEvents = 1; /* for calculating length of graph |
| 194 | bars. */ |
| 195 | int iterations = 0; /* number of weeks/months we iterate |
| 196 | over */ |
| 197 | |
| 198 | char *zCurrentTF; /* The timeframe in which 'now' lives */ |
| 199 | double rNowFraction; /* Fraction of 'now' timeframe that has |
| 200 | passed */ |
| 201 | int nTFChar; /* Prefix of date() for timeframe */ |
| 202 | |
| 203 | nTFChar = includeMonth ? 7 : 4; |
| 204 | stats_report_init_view(); |
| 205 | db_prepare(&query, |
| 206 | "SELECT substr(date(mtime),1,%d) AS timeframe," |
| 207 | " count(*) AS eventCount" |
| 208 | " FROM v_reports" |
| 209 | " WHERE ifnull(coalesce(euser,user,'')=%Q,1)" |
| 210 | " GROUP BY timeframe" |
| 211 | " ORDER BY timeframe DESC", |
| 212 | nTFChar, zUserName); |
| 213 | @ <h1>Timeline Events (%s(stats_report_label_for_type())) |
| 214 | @ by year%s(includeMonth ? "/month" : "") |
| 215 | if( zUserName ){ |
| 216 | @ for user %h(zUserName) |
| 217 | } |
| 218 | @ </h1> |
| 219 | @ <table border='0' cellpadding='2' cellspacing='0' \ |
| 220 | zCurrentTF = db_text(0, "SELECT substr(date(),1,%d)", nTFChar); |
| 221 | if( !includeMonth ){ |
| 222 | @ class='statistics-report-table-events sortable' \ |
| 223 | @ data-column-types='tnx' data-init-sort='0'> |
| 224 | style_table_sorter(); |
| 225 | rNowFraction = db_double(0.5, |
| 226 | "SELECT (unixepoch() - unixepoch('now','start of year'))*1.0/" |
| 227 | " (unixepoch('now','start of year','+1 year') - " |
| 228 | " unixepoch('now','start of year'));"); |
| 229 | }else{ |
| 230 | @ class='statistics-report-table-events'> |
| 231 | rNowFraction = db_double(0.5, |
| 232 | "SELECT (unixepoch() - unixepoch('now','start of month'))*1.0/" |
| 233 | " (unixepoch('now','start of month','+1 month') - " |
| 234 | " unixepoch('now','start of month'));"); |
| 235 | } |
| 236 | @ <thead> |
| 237 | @ <th>%s(zTimeLabel)</th> |
| 238 | @ <th>Events</th> |
| 239 | @ <th width='90%%'><!-- relative commits graph --></th> |
| @@ -231,21 +242,26 @@ | |
| 242 | Run the query twice. The first time we calculate the maximum |
| 243 | number of events for a given row. Maybe someone with better SQL |
| 244 | Fu can re-implement this with a single query. |
| 245 | */ |
| 246 | while( SQLITE_ROW == db_step(&query) ){ |
| 247 | int nCount = db_column_int(&query, 1); |
| 248 | if( strcmp(db_column_text(&query,0),zCurrentTF)==0 |
| 249 | && rNowFraction>0.05 |
| 250 | ){ |
| 251 | nCount = (int)(((double)nCount)/rNowFraction); |
| 252 | } |
| 253 | if(nCount>nMaxEvents){ |
| 254 | nMaxEvents = nCount; |
| 255 | } |
| 256 | ++iterations; |
| 257 | } |
| 258 | db_reset(&query); |
| 259 | while( SQLITE_ROW == db_step(&query) ){ |
| 260 | const char *zTimeframe = db_column_text(&query, 0); |
| 261 | const int nCount = db_column_int(&query, 1); |
| 262 | int nSize = (nCount>0 && nMaxEvents>0) |
| 263 | ? (int)(100 * nCount / nMaxEvents) |
| 264 | : 1; |
| 265 | showYearTotal = 0; |
| 266 | if(!nSize) nSize = 1; |
| 267 | if(includeMonth){ |
| @@ -294,25 +310,30 @@ | |
| 310 | } |
| 311 | cgi_printf("'>%s</a>", zTimeframe); |
| 312 | } |
| 313 | @ </td><td>%d(nCount)</td> |
| 314 | @ <td> |
| 315 | if( strcmp(zTimeframe, zCurrentTF)==0 |
| 316 | && rNowFraction>0.05 |
| 317 | && nCount>0 |
| 318 | && nMaxEvents>0 |
| 319 | ){ |
| 320 | /* If the timespan covered by this row contains "now", then project |
| 321 | ** the number of changes until the completion of the timespan and |
| 322 | ** show a dashed box of that projection. */ |
| 323 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 324 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 325 | @ <span class='statistics-report-graph-line' \ |
| 326 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 327 | @ <span class='statistics-report-graph-extra' \ |
| 328 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 329 | }else{ |
| 330 | @ <div class='statistics-report-graph-line' \ |
| 331 | @ style='width:%d(nSize)%%;'> </div> \ |
| 332 | } |
| 333 | @ </td> |
| 334 | @ </tr> |
| 335 | /* |
| 336 | Potential improvement: calculate the min/max event counts and |
| 337 | use percent-based graph bars. |
| 338 | */ |
| 339 | } |
| @@ -327,12 +348,12 @@ | |
| 348 | } |
| 349 | @ </tbody></table> |
| 350 | if(nEventTotal){ |
| 351 | const char *zAvgLabel = includeMonth ? "month" : "year"; |
| 352 | int nAvg = iterations ? (nEventTotal/iterations) : 0; |
| 353 | @ <br><div>Total events: %d(nEventTotal) |
| 354 | @ <br>Average per active %s(zAvgLabel): %d(nAvg) |
| 355 | @ </div> |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | /* |
| @@ -354,11 +375,11 @@ | |
| 375 | " GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;" |
| 376 | ); |
| 377 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 378 | @ <center><svg width=700 height=400> |
| 379 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 380 | @ </svg></centre><hr> |
| 381 | } |
| 382 | style_table_sorter(); |
| 383 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 384 | @ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'> |
| 385 | @ <thead><tr> |
| @@ -510,11 +531,11 @@ | |
| 531 | , zUserName |
| 532 | ); |
| 533 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 534 | @ <center><svg width=700 height=400> |
| 535 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 536 | @ </svg></centre><hr> |
| 537 | } |
| 538 | style_table_sorter(); |
| 539 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 540 | @ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'> |
| 541 | @ <thead><tr> |
| @@ -587,11 +608,11 @@ | |
| 608 | zUserName |
| 609 | ); |
| 610 | if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){ |
| 611 | @ <center><svg width=700 height=400> |
| 612 | piechart_render(700, 400, PIE_OTHER|PIE_PERCENT); |
| 613 | @ </svg></centre><hr> |
| 614 | } |
| 615 | style_table_sorter(); |
| 616 | @ <table class='statistics-report-table-events sortable' border='0' \ |
| 617 | @ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'> |
| 618 | @ <thead><tr> |
| @@ -629,12 +650,12 @@ | |
| 650 | } |
| 651 | |
| 652 | |
| 653 | /* |
| 654 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 655 | ** week numbers. The "y" query parameter is the year in format YYYY. |
| 656 | ** If zUserName is not NULL then the report is restricted to events |
| 657 | ** created by the named user account. |
| 658 | */ |
| 659 | static void stats_report_year_weeks(const char *zUserName){ |
| 660 | const char *zYear = P("y"); /* Year for which report shown */ |
| 661 | Stmt q; |
| @@ -641,10 +662,13 @@ | |
| 662 | int nMaxEvents = 1; /* max number of events for |
| 663 | all rows. */ |
| 664 | int iterations = 0; /* # of active time periods. */ |
| 665 | int rowCount = 0; |
| 666 | int total = 0; |
| 667 | char *zCurrentWeek; /* Current week number */ |
| 668 | double rNowFraction = 0.0; /* Fraction of current week that has |
| 669 | ** passed */ |
| 670 | |
| 671 | stats_report_init_view(); |
| 672 | style_submenu_sql("y", "Year:", |
| 673 | "WITH RECURSIVE a(b) AS (" |
| 674 | " SELECT substr(date('now'),1,4) UNION ALL" |
| @@ -652,11 +676,11 @@ | |
| 676 | " WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)" |
| 677 | ") SELECT b, b FROM a ORDER BY b DESC"); |
| 678 | if( zYear==0 || strlen(zYear)!=4 ){ |
| 679 | zYear = db_text("1970","SELECT substr(date('now'),1,4);"); |
| 680 | } |
| 681 | cgi_printf("<br>\n"); |
| 682 | db_prepare(&q, |
| 683 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 684 | " count(*) AS n " |
| 685 | " FROM v_reports " |
| 686 | " WHERE %Q=substr(date(mtime),1,4) " |
| @@ -667,32 +691,45 @@ | |
| 691 | @ for the calendar weeks of %h(zYear) |
| 692 | if( zUserName ){ |
| 693 | @ for user %h(zUserName) |
| 694 | } |
| 695 | @ </h1> |
| 696 | zCurrentWeek = db_text(0, |
| 697 | "SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'", |
| 698 | zYear); |
| 699 | if( zCurrentWeek ){ |
| 700 | rNowFraction = db_double(0.5, |
| 701 | "SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;"); |
| 702 | } |
| 703 | style_table_sorter(); |
| 704 | cgi_printf("<table class='statistics-report-table-events sortable' " |
| 705 | "border='0' cellpadding='2' width='100%%' " |
| 706 | "cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n"); |
| 707 | cgi_printf("<thead><tr>" |
| 708 | "<th>Week</th>" |
| 709 | "<th>Events</th>" |
| 710 | "<th width='90%%'><!-- relative commits graph --></th>" |
| 711 | "</tr></thead>\n" |
| 712 | "<tbody>\n"); |
| 713 | while( SQLITE_ROW == db_step(&q) ){ |
| 714 | int nCount = db_column_int(&q, 1); |
| 715 | if( zCurrentWeek!=0 |
| 716 | && strcmp(db_column_text(&q,0),zCurrentWeek)==0 |
| 717 | && rNowFraction>0.05 |
| 718 | ){ |
| 719 | nCount = (int)(((double)nCount)/rNowFraction); |
| 720 | } |
| 721 | if(nCount>nMaxEvents){ |
| 722 | nMaxEvents = nCount; |
| 723 | } |
| 724 | ++iterations; |
| 725 | } |
| 726 | db_reset(&q); |
| 727 | while( SQLITE_ROW == db_step(&q) ){ |
| 728 | const char *zWeek = db_column_text(&q,0); |
| 729 | const int nCount = db_column_int(&q,1); |
| 730 | int nSize = (nCount>0 && nMaxEvents>0) |
| 731 | ? (int)(100 * nCount / nMaxEvents) |
| 732 | : 0; |
| 733 | if(!nSize) nSize = 1; |
| 734 | total += nCount; |
| 735 | cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); |
| @@ -704,22 +741,37 @@ | |
| 741 | } |
| 742 | cgi_printf("'>%s</a></td>",zWeek); |
| 743 | |
| 744 | cgi_printf("<td>%d</td>",nCount); |
| 745 | cgi_printf("<td>"); |
| 746 | if( nCount ){ |
| 747 | if( zCurrentWeek!=0 |
| 748 | && strcmp(zWeek, zCurrentWeek)==0 |
| 749 | && rNowFraction>0.05 |
| 750 | && nMaxEvents>0 |
| 751 | ){ |
| 752 | /* If the covered covered by this row contains "now", then project |
| 753 | ** the number of changes until the completion of the week and |
| 754 | ** show a dashed box of that projection. */ |
| 755 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 756 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 757 | @ <span class='statistics-report-graph-line' \ |
| 758 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 759 | @ <span class='statistics-report-graph-extra' \ |
| 760 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 761 | }else{ |
| 762 | @ <div class='statistics-report-graph-line' \ |
| 763 | @ style='width:%d(nSize)%%;'> </div> \ |
| 764 | } |
| 765 | } |
| 766 | cgi_printf("</td></tr>\n"); |
| 767 | } |
| 768 | db_finalize(&q); |
| 769 | cgi_printf("</tbody></table>"); |
| 770 | if(total){ |
| 771 | int nAvg = iterations ? (total/iterations) : 0; |
| 772 | cgi_printf("<br><div>Total events: %d<br>" |
| 773 | "Average per active week: %d</div>", |
| 774 | total, nAvg); |
| 775 | } |
| 776 | } |
| 777 | |
| @@ -785,14 +837,29 @@ | |
| 837 | ** |
| 838 | ** Shows activity reports for the repository. |
| 839 | ** |
| 840 | ** Query Parameters: |
| 841 | ** |
| 842 | ** view=REPORT_NAME Valid REPORT_NAME values: |
| 843 | ** * byyear |
| 844 | ** * bymonth |
| 845 | ** * byweek |
| 846 | ** * byweekday |
| 847 | ** * byhour |
| 848 | ** * byuser |
| 849 | ** * byfile |
| 850 | ** * lastchng |
| 851 | ** user=NAME Restricts statistics to the given user |
| 852 | ** type=TYPE Restricts the report to a specific event type: |
| 853 | ** * all (everything), |
| 854 | ** * ci (check-in) |
| 855 | ** * m (merge check-in), |
| 856 | ** * n (non-merge check-in) |
| 857 | ** * f (forum post) |
| 858 | ** * w (wiki page change) |
| 859 | ** * t (ticket change) |
| 860 | ** * g (tag added or removed) |
| 861 | ** Defaulting to all event types. |
| 862 | ** |
| 863 | ** The view-specific query parameters include: |
| 864 | ** |
| 865 | ** view=byweek: |
| @@ -822,10 +889,12 @@ | |
| 889 | }; |
| 890 | static const char *const azType[] = { |
| 891 | "a", "All Changes", |
| 892 | "ci", "Check-ins", |
| 893 | "f", "Forum Posts", |
| 894 | "m", "Merge check-ins", |
| 895 | "n", "Non-merge check-ins", |
| 896 | "g", "Tags", |
| 897 | "e", "Tech Notes", |
| 898 | "t", "Tickets", |
| 899 | "w", "Wiki" |
| 900 | }; |
| @@ -867,14 +936,14 @@ | |
| 936 | } |
| 937 | style_submenu_element("Stats", "%R/stat"); |
| 938 | style_header("Activity Reports"); |
| 939 | switch( eType ){ |
| 940 | case RPT_BYYEAR: |
| 941 | stats_report_by_month_year(0, zUserName); |
| 942 | break; |
| 943 | case RPT_BYMONTH: |
| 944 | stats_report_by_month_year(1, zUserName); |
| 945 | break; |
| 946 | case RPT_BYWEEK: |
| 947 | stats_report_year_weeks(zUserName); |
| 948 | break; |
| 949 | default: |
| 950 |
+33
-33
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -648,17 +648,17 @@ | ||
| 648 | 648 | */ |
| 649 | 649 | static const char zDfltHeader[] = |
| 650 | 650 | @ <html> |
| 651 | 651 | @ <head> |
| 652 | 652 | @ <meta charset="UTF-8"> |
| 653 | -@ <base href="$baseurl/$current_page" /> | |
| 654 | -@ <meta http-equiv="Content-Security-Policy" content="$default_csp" /> | |
| 653 | +@ <base href="$baseurl/$current_page"> | |
| 654 | +@ <meta http-equiv="Content-Security-Policy" content="$default_csp"> | |
| 655 | 655 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 656 | 656 | @ <title>$<project_name>: $<title></title> |
| 657 | 657 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 658 | -@ href="$home/timeline.rss" /> | |
| 659 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" /> | |
| 658 | +@ href="$home/timeline.rss"> | |
| 659 | +@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"> | |
| 660 | 660 | @ </head> |
| 661 | 661 | @ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> |
| 662 | 662 | ; |
| 663 | 663 | |
| 664 | 664 | /* |
| @@ -817,20 +817,20 @@ | ||
| 817 | 817 | |
| 818 | 818 | cgi_destination(CGI_HEADER); |
| 819 | 819 | |
| 820 | 820 | @ <!DOCTYPE html> |
| 821 | 821 | |
| 822 | - if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); | |
| 822 | + if( g.thTrace ) Th_Trace("BEGIN_HEADER<br>\n", -1); | |
| 823 | 823 | |
| 824 | 824 | /* Generate the header up through the main menu */ |
| 825 | 825 | style_init_th1_vars(zTitle); |
| 826 | 826 | if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ |
| 827 | 827 | Th_Render(zDfltHeader); |
| 828 | 828 | } |
| 829 | - if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); | |
| 829 | + if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1); | |
| 830 | 830 | Th_Render(zHeader); |
| 831 | - if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); | |
| 831 | + if( g.thTrace ) Th_Trace("END_HEADER<br>\n", -1); | |
| 832 | 832 | Th_Unstore("title"); /* Avoid collisions with ticket field names */ |
| 833 | 833 | cgi_destination(CGI_BODY); |
| 834 | 834 | g.cgiOutput = 1; |
| 835 | 835 | headerHasBeenGenerated = 1; |
| 836 | 836 | sideboxUsed = 0; |
| @@ -1121,17 +1121,17 @@ | ||
| 1121 | 1121 | /* Put the footer at the bottom of the page. */ |
| 1122 | 1122 | zFooter = skin_get("footer"); |
| 1123 | 1123 | if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){ |
| 1124 | 1124 | style_load_all_js_files(); |
| 1125 | 1125 | } |
| 1126 | - if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); | |
| 1126 | + if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1); | |
| 1127 | 1127 | Th_Render(zFooter); |
| 1128 | - if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); | |
| 1128 | + if( g.thTrace ) Th_Trace("END_FOOTER<br>\n", -1); | |
| 1129 | 1129 | |
| 1130 | 1130 | /* Render trace log if TH1 tracing is enabled. */ |
| 1131 | 1131 | if( g.thTrace ){ |
| 1132 | - cgi_append_content("<span class=\"thTrace\"><hr />\n", -1); | |
| 1132 | + cgi_append_content("<span class=\"thTrace\"><hr>\n", -1); | |
| 1133 | 1133 | cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); |
| 1134 | 1134 | cgi_append_content("</span>\n", -1); |
| 1135 | 1135 | } |
| 1136 | 1136 | |
| 1137 | 1137 | /* Add document end mark if it was not in the footer */ |
| @@ -1429,58 +1429,58 @@ | ||
| 1429 | 1429 | style_submenu_element("Stats", "%R/stat"); |
| 1430 | 1430 | } |
| 1431 | 1431 | |
| 1432 | 1432 | if( isAuth ){ |
| 1433 | 1433 | #if !defined(_WIN32) |
| 1434 | - @ uid=%d(getuid()), gid=%d(getgid())<br /> | |
| 1434 | + @ uid=%d(getuid()), gid=%d(getgid())<br> | |
| 1435 | 1435 | #endif |
| 1436 | - @ g.zBaseURL = %h(g.zBaseURL)<br /> | |
| 1437 | - @ g.zHttpsURL = %h(g.zHttpsURL)<br /> | |
| 1438 | - @ g.zTop = %h(g.zTop)<br /> | |
| 1439 | - @ g.zPath = %h(g.zPath)<br /> | |
| 1440 | - @ g.userUid = %d(g.userUid)<br /> | |
| 1441 | - @ g.zLogin = %h(g.zLogin)<br /> | |
| 1442 | - @ g.isHuman = %d(g.isHuman)<br /> | |
| 1443 | - @ g.jsHref = %d(g.jsHref)<br /> | |
| 1436 | + @ g.zBaseURL = %h(g.zBaseURL)<br> | |
| 1437 | + @ g.zHttpsURL = %h(g.zHttpsURL)<br> | |
| 1438 | + @ g.zTop = %h(g.zTop)<br> | |
| 1439 | + @ g.zPath = %h(g.zPath)<br> | |
| 1440 | + @ g.userUid = %d(g.userUid)<br> | |
| 1441 | + @ g.zLogin = %h(g.zLogin)<br> | |
| 1442 | + @ g.isHuman = %d(g.isHuman)<br> | |
| 1443 | + @ g.jsHref = %d(g.jsHref)<br> | |
| 1444 | 1444 | if( g.zLocalRoot ){ |
| 1445 | - @ g.zLocalRoot = %h(g.zLocalRoot)<br /> | |
| 1445 | + @ g.zLocalRoot = %h(g.zLocalRoot)<br> | |
| 1446 | 1446 | }else{ |
| 1447 | - @ g.zLocalRoot = <i>none</i><br /> | |
| 1447 | + @ g.zLocalRoot = <i>none</i><br> | |
| 1448 | 1448 | } |
| 1449 | 1449 | if( g.nRequest ){ |
| 1450 | - @ g.nRequest = %d(g.nRequest)<br /> | |
| 1450 | + @ g.nRequest = %d(g.nRequest)<br> | |
| 1451 | 1451 | } |
| 1452 | 1452 | if( g.nPendingRequest>1 ){ |
| 1453 | - @ g.nPendingRequest = %d(g.nPendingRequest)<br /> | |
| 1453 | + @ g.nPendingRequest = %d(g.nPendingRequest)<br> | |
| 1454 | 1454 | } |
| 1455 | - @ capabilities = %s(find_capabilities(zCap))<br /> | |
| 1455 | + @ capabilities = %s(find_capabilities(zCap))<br> | |
| 1456 | 1456 | if( zCap[0] ){ |
| 1457 | - @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> | |
| 1457 | + @ anonymous-adds = %s(find_anon_capabilities(zCap))<br> | |
| 1458 | 1458 | } |
| 1459 | - @ g.zRepositoryName = %h(g.zRepositoryName)<br /> | |
| 1460 | - @ load_average() = %f(load_average())<br /> | |
| 1459 | + @ g.zRepositoryName = %h(g.zRepositoryName)<br> | |
| 1460 | + @ load_average() = %f(load_average())<br> | |
| 1461 | 1461 | #ifndef _WIN32 |
| 1462 | - @ RSS = %.2f(fossil_rss()/1000000.0) MB</br /> | |
| 1462 | + @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> | |
| 1463 | 1463 | #endif |
| 1464 | - @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> | |
| 1465 | - @ fossil_exe_id() = %h(fossil_exe_id())<br /> | |
| 1464 | + @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br> | |
| 1465 | + @ fossil_exe_id() = %h(fossil_exe_id())<br> | |
| 1466 | 1466 | if( g.perm.Admin ){ |
| 1467 | 1467 | int k; |
| 1468 | 1468 | for(k=0; g.argvOrig[k]; k++){ |
| 1469 | 1469 | Blob t; |
| 1470 | 1470 | blob_init(&t, 0, 0); |
| 1471 | 1471 | blob_append_escaped_arg(&t, g.argvOrig[k], 0); |
| 1472 | - @ argv[%d(k)] = %h(blob_str(&t))<br /> | |
| 1472 | + @ argv[%d(k)] = %h(blob_str(&t))<br> | |
| 1473 | 1473 | blob_zero(&t); |
| 1474 | 1474 | } |
| 1475 | 1475 | } |
| 1476 | - @ <hr /> | |
| 1476 | + @ <hr> | |
| 1477 | 1477 | P("HTTP_USER_AGENT"); |
| 1478 | 1478 | P("SERVER_SOFTWARE"); |
| 1479 | 1479 | cgi_print_all(showAll, 0); |
| 1480 | 1480 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1481 | - @ <hr /> | |
| 1481 | + @ <hr> | |
| 1482 | 1482 | @ <pre> |
| 1483 | 1483 | @ %h(blob_str(&g.httpHeader)) |
| 1484 | 1484 | @ </pre> |
| 1485 | 1485 | } |
| 1486 | 1486 | } |
| 1487 | 1487 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -648,17 +648,17 @@ | |
| 648 | */ |
| 649 | static const char zDfltHeader[] = |
| 650 | @ <html> |
| 651 | @ <head> |
| 652 | @ <meta charset="UTF-8"> |
| 653 | @ <base href="$baseurl/$current_page" /> |
| 654 | @ <meta http-equiv="Content-Security-Policy" content="$default_csp" /> |
| 655 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 656 | @ <title>$<project_name>: $<title></title> |
| 657 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 658 | @ href="$home/timeline.rss" /> |
| 659 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" /> |
| 660 | @ </head> |
| 661 | @ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> |
| 662 | ; |
| 663 | |
| 664 | /* |
| @@ -817,20 +817,20 @@ | |
| 817 | |
| 818 | cgi_destination(CGI_HEADER); |
| 819 | |
| 820 | @ <!DOCTYPE html> |
| 821 | |
| 822 | if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); |
| 823 | |
| 824 | /* Generate the header up through the main menu */ |
| 825 | style_init_th1_vars(zTitle); |
| 826 | if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ |
| 827 | Th_Render(zDfltHeader); |
| 828 | } |
| 829 | if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); |
| 830 | Th_Render(zHeader); |
| 831 | if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); |
| 832 | Th_Unstore("title"); /* Avoid collisions with ticket field names */ |
| 833 | cgi_destination(CGI_BODY); |
| 834 | g.cgiOutput = 1; |
| 835 | headerHasBeenGenerated = 1; |
| 836 | sideboxUsed = 0; |
| @@ -1121,17 +1121,17 @@ | |
| 1121 | /* Put the footer at the bottom of the page. */ |
| 1122 | zFooter = skin_get("footer"); |
| 1123 | if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){ |
| 1124 | style_load_all_js_files(); |
| 1125 | } |
| 1126 | if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); |
| 1127 | Th_Render(zFooter); |
| 1128 | if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); |
| 1129 | |
| 1130 | /* Render trace log if TH1 tracing is enabled. */ |
| 1131 | if( g.thTrace ){ |
| 1132 | cgi_append_content("<span class=\"thTrace\"><hr />\n", -1); |
| 1133 | cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); |
| 1134 | cgi_append_content("</span>\n", -1); |
| 1135 | } |
| 1136 | |
| 1137 | /* Add document end mark if it was not in the footer */ |
| @@ -1429,58 +1429,58 @@ | |
| 1429 | style_submenu_element("Stats", "%R/stat"); |
| 1430 | } |
| 1431 | |
| 1432 | if( isAuth ){ |
| 1433 | #if !defined(_WIN32) |
| 1434 | @ uid=%d(getuid()), gid=%d(getgid())<br /> |
| 1435 | #endif |
| 1436 | @ g.zBaseURL = %h(g.zBaseURL)<br /> |
| 1437 | @ g.zHttpsURL = %h(g.zHttpsURL)<br /> |
| 1438 | @ g.zTop = %h(g.zTop)<br /> |
| 1439 | @ g.zPath = %h(g.zPath)<br /> |
| 1440 | @ g.userUid = %d(g.userUid)<br /> |
| 1441 | @ g.zLogin = %h(g.zLogin)<br /> |
| 1442 | @ g.isHuman = %d(g.isHuman)<br /> |
| 1443 | @ g.jsHref = %d(g.jsHref)<br /> |
| 1444 | if( g.zLocalRoot ){ |
| 1445 | @ g.zLocalRoot = %h(g.zLocalRoot)<br /> |
| 1446 | }else{ |
| 1447 | @ g.zLocalRoot = <i>none</i><br /> |
| 1448 | } |
| 1449 | if( g.nRequest ){ |
| 1450 | @ g.nRequest = %d(g.nRequest)<br /> |
| 1451 | } |
| 1452 | if( g.nPendingRequest>1 ){ |
| 1453 | @ g.nPendingRequest = %d(g.nPendingRequest)<br /> |
| 1454 | } |
| 1455 | @ capabilities = %s(find_capabilities(zCap))<br /> |
| 1456 | if( zCap[0] ){ |
| 1457 | @ anonymous-adds = %s(find_anon_capabilities(zCap))<br /> |
| 1458 | } |
| 1459 | @ g.zRepositoryName = %h(g.zRepositoryName)<br /> |
| 1460 | @ load_average() = %f(load_average())<br /> |
| 1461 | #ifndef _WIN32 |
| 1462 | @ RSS = %.2f(fossil_rss()/1000000.0) MB</br /> |
| 1463 | #endif |
| 1464 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br /> |
| 1465 | @ fossil_exe_id() = %h(fossil_exe_id())<br /> |
| 1466 | if( g.perm.Admin ){ |
| 1467 | int k; |
| 1468 | for(k=0; g.argvOrig[k]; k++){ |
| 1469 | Blob t; |
| 1470 | blob_init(&t, 0, 0); |
| 1471 | blob_append_escaped_arg(&t, g.argvOrig[k], 0); |
| 1472 | @ argv[%d(k)] = %h(blob_str(&t))<br /> |
| 1473 | blob_zero(&t); |
| 1474 | } |
| 1475 | } |
| 1476 | @ <hr /> |
| 1477 | P("HTTP_USER_AGENT"); |
| 1478 | P("SERVER_SOFTWARE"); |
| 1479 | cgi_print_all(showAll, 0); |
| 1480 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1481 | @ <hr /> |
| 1482 | @ <pre> |
| 1483 | @ %h(blob_str(&g.httpHeader)) |
| 1484 | @ </pre> |
| 1485 | } |
| 1486 | } |
| 1487 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -648,17 +648,17 @@ | |
| 648 | */ |
| 649 | static const char zDfltHeader[] = |
| 650 | @ <html> |
| 651 | @ <head> |
| 652 | @ <meta charset="UTF-8"> |
| 653 | @ <base href="$baseurl/$current_page"> |
| 654 | @ <meta http-equiv="Content-Security-Policy" content="$default_csp"> |
| 655 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 656 | @ <title>$<project_name>: $<title></title> |
| 657 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 658 | @ href="$home/timeline.rss"> |
| 659 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"> |
| 660 | @ </head> |
| 661 | @ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> |
| 662 | ; |
| 663 | |
| 664 | /* |
| @@ -817,20 +817,20 @@ | |
| 817 | |
| 818 | cgi_destination(CGI_HEADER); |
| 819 | |
| 820 | @ <!DOCTYPE html> |
| 821 | |
| 822 | if( g.thTrace ) Th_Trace("BEGIN_HEADER<br>\n", -1); |
| 823 | |
| 824 | /* Generate the header up through the main menu */ |
| 825 | style_init_th1_vars(zTitle); |
| 826 | if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ |
| 827 | Th_Render(zDfltHeader); |
| 828 | } |
| 829 | if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1); |
| 830 | Th_Render(zHeader); |
| 831 | if( g.thTrace ) Th_Trace("END_HEADER<br>\n", -1); |
| 832 | Th_Unstore("title"); /* Avoid collisions with ticket field names */ |
| 833 | cgi_destination(CGI_BODY); |
| 834 | g.cgiOutput = 1; |
| 835 | headerHasBeenGenerated = 1; |
| 836 | sideboxUsed = 0; |
| @@ -1121,17 +1121,17 @@ | |
| 1121 | /* Put the footer at the bottom of the page. */ |
| 1122 | zFooter = skin_get("footer"); |
| 1123 | if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){ |
| 1124 | style_load_all_js_files(); |
| 1125 | } |
| 1126 | if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1); |
| 1127 | Th_Render(zFooter); |
| 1128 | if( g.thTrace ) Th_Trace("END_FOOTER<br>\n", -1); |
| 1129 | |
| 1130 | /* Render trace log if TH1 tracing is enabled. */ |
| 1131 | if( g.thTrace ){ |
| 1132 | cgi_append_content("<span class=\"thTrace\"><hr>\n", -1); |
| 1133 | cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); |
| 1134 | cgi_append_content("</span>\n", -1); |
| 1135 | } |
| 1136 | |
| 1137 | /* Add document end mark if it was not in the footer */ |
| @@ -1429,58 +1429,58 @@ | |
| 1429 | style_submenu_element("Stats", "%R/stat"); |
| 1430 | } |
| 1431 | |
| 1432 | if( isAuth ){ |
| 1433 | #if !defined(_WIN32) |
| 1434 | @ uid=%d(getuid()), gid=%d(getgid())<br> |
| 1435 | #endif |
| 1436 | @ g.zBaseURL = %h(g.zBaseURL)<br> |
| 1437 | @ g.zHttpsURL = %h(g.zHttpsURL)<br> |
| 1438 | @ g.zTop = %h(g.zTop)<br> |
| 1439 | @ g.zPath = %h(g.zPath)<br> |
| 1440 | @ g.userUid = %d(g.userUid)<br> |
| 1441 | @ g.zLogin = %h(g.zLogin)<br> |
| 1442 | @ g.isHuman = %d(g.isHuman)<br> |
| 1443 | @ g.jsHref = %d(g.jsHref)<br> |
| 1444 | if( g.zLocalRoot ){ |
| 1445 | @ g.zLocalRoot = %h(g.zLocalRoot)<br> |
| 1446 | }else{ |
| 1447 | @ g.zLocalRoot = <i>none</i><br> |
| 1448 | } |
| 1449 | if( g.nRequest ){ |
| 1450 | @ g.nRequest = %d(g.nRequest)<br> |
| 1451 | } |
| 1452 | if( g.nPendingRequest>1 ){ |
| 1453 | @ g.nPendingRequest = %d(g.nPendingRequest)<br> |
| 1454 | } |
| 1455 | @ capabilities = %s(find_capabilities(zCap))<br> |
| 1456 | if( zCap[0] ){ |
| 1457 | @ anonymous-adds = %s(find_anon_capabilities(zCap))<br> |
| 1458 | } |
| 1459 | @ g.zRepositoryName = %h(g.zRepositoryName)<br> |
| 1460 | @ load_average() = %f(load_average())<br> |
| 1461 | #ifndef _WIN32 |
| 1462 | @ RSS = %.2f(fossil_rss()/1000000.0) MB</br> |
| 1463 | #endif |
| 1464 | @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br> |
| 1465 | @ fossil_exe_id() = %h(fossil_exe_id())<br> |
| 1466 | if( g.perm.Admin ){ |
| 1467 | int k; |
| 1468 | for(k=0; g.argvOrig[k]; k++){ |
| 1469 | Blob t; |
| 1470 | blob_init(&t, 0, 0); |
| 1471 | blob_append_escaped_arg(&t, g.argvOrig[k], 0); |
| 1472 | @ argv[%d(k)] = %h(blob_str(&t))<br> |
| 1473 | blob_zero(&t); |
| 1474 | } |
| 1475 | } |
| 1476 | @ <hr> |
| 1477 | P("HTTP_USER_AGENT"); |
| 1478 | P("SERVER_SOFTWARE"); |
| 1479 | cgi_print_all(showAll, 0); |
| 1480 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| 1481 | @ <hr> |
| 1482 | @ <pre> |
| 1483 | @ %h(blob_str(&g.httpHeader)) |
| 1484 | @ </pre> |
| 1485 | } |
| 1486 | } |
| 1487 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -886,11 +886,11 @@ | ||
| 886 | 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | 890 | db_finalize(&q); |
| 891 | - @ <br /> | |
| 891 | + @ <br> | |
| 892 | 892 | style_finish_page(); |
| 893 | 893 | } |
| 894 | 894 | |
| 895 | 895 | /* |
| 896 | 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 | 897 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -886,11 +886,11 @@ | |
| 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | db_finalize(&q); |
| 891 | @ <br /> |
| 892 | style_finish_page(); |
| 893 | } |
| 894 | |
| 895 | /* |
| 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -886,11 +886,11 @@ | |
| 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | db_finalize(&q); |
| 891 | @ <br> |
| 892 | style_finish_page(); |
| 893 | } |
| 894 | |
| 895 | /* |
| 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -886,11 +886,11 @@ | ||
| 886 | 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | 890 | db_finalize(&q); |
| 891 | - @ <br /> | |
| 891 | + @ <br> | |
| 892 | 892 | style_finish_page(); |
| 893 | 893 | } |
| 894 | 894 | |
| 895 | 895 | /* |
| 896 | 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 | 897 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -886,11 +886,11 @@ | |
| 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | db_finalize(&q); |
| 891 | @ <br /> |
| 892 | style_finish_page(); |
| 893 | } |
| 894 | |
| 895 | /* |
| 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -886,11 +886,11 @@ | |
| 886 | if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH; |
| 887 | if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR; |
| 888 | if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR; |
| 889 | www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); |
| 890 | db_finalize(&q); |
| 891 | @ <br> |
| 892 | style_finish_page(); |
| 893 | } |
| 894 | |
| 895 | /* |
| 896 | ** Returns true if the given blob.rid value has the given tag ID |
| 897 |
+5
-5
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -808,17 +808,17 @@ | ||
| 808 | 808 | zKey = blob_str(&cacheKey); |
| 809 | 809 | etag_check(ETAG_HASH, zKey); |
| 810 | 810 | |
| 811 | 811 | if( P("debug")!=0 ){ |
| 812 | 812 | style_header("Tarball Generator Debug Screen"); |
| 813 | - @ zName = "%h(zName)"<br /> | |
| 814 | - @ rid = %d(rid)<br /> | |
| 813 | + @ zName = "%h(zName)"<br> | |
| 814 | + @ rid = %d(rid)<br> | |
| 815 | 815 | if( zInclude ){ |
| 816 | - @ zInclude = "%h(zInclude)"<br /> | |
| 816 | + @ zInclude = "%h(zInclude)"<br> | |
| 817 | 817 | } |
| 818 | 818 | if( zExclude ){ |
| 819 | - @ zExclude = "%h(zExclude)"<br /> | |
| 819 | + @ zExclude = "%h(zExclude)"<br> | |
| 820 | 820 | } |
| 821 | 821 | @ zKey = "%h(zKey)" |
| 822 | 822 | style_finish_page(); |
| 823 | 823 | return; |
| 824 | 824 | } |
| @@ -826,11 +826,11 @@ | ||
| 826 | 826 | style_header("Tarball Download"); |
| 827 | 827 | @ <form action='%R/tarball/%h(zName).tar.gz'> |
| 828 | 828 | cgi_query_parameters_to_hidden(); |
| 829 | 829 | @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content |
| 830 | 830 | @ of check-in <b>%h(zRid)</b>: |
| 831 | - @ <input type="submit" value="Download" /> | |
| 831 | + @ <input type="submit" value="Download"> | |
| 832 | 832 | @ </form> |
| 833 | 833 | style_finish_page(); |
| 834 | 834 | return; |
| 835 | 835 | } |
| 836 | 836 | blob_zero(&tarball); |
| 837 | 837 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -808,17 +808,17 @@ | |
| 808 | zKey = blob_str(&cacheKey); |
| 809 | etag_check(ETAG_HASH, zKey); |
| 810 | |
| 811 | if( P("debug")!=0 ){ |
| 812 | style_header("Tarball Generator Debug Screen"); |
| 813 | @ zName = "%h(zName)"<br /> |
| 814 | @ rid = %d(rid)<br /> |
| 815 | if( zInclude ){ |
| 816 | @ zInclude = "%h(zInclude)"<br /> |
| 817 | } |
| 818 | if( zExclude ){ |
| 819 | @ zExclude = "%h(zExclude)"<br /> |
| 820 | } |
| 821 | @ zKey = "%h(zKey)" |
| 822 | style_finish_page(); |
| 823 | return; |
| 824 | } |
| @@ -826,11 +826,11 @@ | |
| 826 | style_header("Tarball Download"); |
| 827 | @ <form action='%R/tarball/%h(zName).tar.gz'> |
| 828 | cgi_query_parameters_to_hidden(); |
| 829 | @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content |
| 830 | @ of check-in <b>%h(zRid)</b>: |
| 831 | @ <input type="submit" value="Download" /> |
| 832 | @ </form> |
| 833 | style_finish_page(); |
| 834 | return; |
| 835 | } |
| 836 | blob_zero(&tarball); |
| 837 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -808,17 +808,17 @@ | |
| 808 | zKey = blob_str(&cacheKey); |
| 809 | etag_check(ETAG_HASH, zKey); |
| 810 | |
| 811 | if( P("debug")!=0 ){ |
| 812 | style_header("Tarball Generator Debug Screen"); |
| 813 | @ zName = "%h(zName)"<br> |
| 814 | @ rid = %d(rid)<br> |
| 815 | if( zInclude ){ |
| 816 | @ zInclude = "%h(zInclude)"<br> |
| 817 | } |
| 818 | if( zExclude ){ |
| 819 | @ zExclude = "%h(zExclude)"<br> |
| 820 | } |
| 821 | @ zKey = "%h(zKey)" |
| 822 | style_finish_page(); |
| 823 | return; |
| 824 | } |
| @@ -826,11 +826,11 @@ | |
| 826 | style_header("Tarball Download"); |
| 827 | @ <form action='%R/tarball/%h(zName).tar.gz'> |
| 828 | cgi_query_parameters_to_hidden(); |
| 829 | @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content |
| 830 | @ of check-in <b>%h(zRid)</b>: |
| 831 | @ <input type="submit" value="Download"> |
| 832 | @ </form> |
| 833 | style_finish_page(); |
| 834 | return; |
| 835 | } |
| 836 | blob_zero(&tarball); |
| 837 |
M
src/th.c
+21
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1269,10 +1269,31 @@ | ||
| 1269 | 1269 | return TH_ERROR; |
| 1270 | 1270 | } |
| 1271 | 1271 | |
| 1272 | 1272 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1273 | 1273 | } |
| 1274 | + | |
| 1275 | +/* | |
| 1276 | +** If interp has a variable with the given name, its value is returned | |
| 1277 | +** and its length is returned via *nOut if nOut is not NULL. If | |
| 1278 | +** interp has no such var then NULL is returned without setting any | |
| 1279 | +** error state and *nOut, if not NULL, is set to -1. The returned value | |
| 1280 | +** is owned by the interpreter and may be invalidated the next time | |
| 1281 | +** the interpreter is modified. | |
| 1282 | +*/ | |
| 1283 | +const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, | |
| 1284 | + int *nOut){ | |
| 1285 | + Th_Variable *pValue; | |
| 1286 | + | |
| 1287 | + pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0); | |
| 1288 | + if( !pValue || !pValue->zData ){ | |
| 1289 | + if( nOut!=0 ) *nOut = -1; | |
| 1290 | + return NULL; | |
| 1291 | + } | |
| 1292 | + if( nOut!=0 ) *nOut = pValue->nData; | |
| 1293 | + return pValue->zData; | |
| 1294 | +} | |
| 1274 | 1295 | |
| 1275 | 1296 | /* |
| 1276 | 1297 | ** Return true if variable (zVar, nVar) exists. |
| 1277 | 1298 | */ |
| 1278 | 1299 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1279 | 1300 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1269,10 +1269,31 @@ | |
| 1269 | return TH_ERROR; |
| 1270 | } |
| 1271 | |
| 1272 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1273 | } |
| 1274 | |
| 1275 | /* |
| 1276 | ** Return true if variable (zVar, nVar) exists. |
| 1277 | */ |
| 1278 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1279 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1269,10 +1269,31 @@ | |
| 1269 | return TH_ERROR; |
| 1270 | } |
| 1271 | |
| 1272 | return Th_SetResult(interp, pValue->zData, pValue->nData); |
| 1273 | } |
| 1274 | |
| 1275 | /* |
| 1276 | ** If interp has a variable with the given name, its value is returned |
| 1277 | ** and its length is returned via *nOut if nOut is not NULL. If |
| 1278 | ** interp has no such var then NULL is returned without setting any |
| 1279 | ** error state and *nOut, if not NULL, is set to -1. The returned value |
| 1280 | ** is owned by the interpreter and may be invalidated the next time |
| 1281 | ** the interpreter is modified. |
| 1282 | */ |
| 1283 | const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, |
| 1284 | int *nOut){ |
| 1285 | Th_Variable *pValue; |
| 1286 | |
| 1287 | pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0); |
| 1288 | if( !pValue || !pValue->zData ){ |
| 1289 | if( nOut!=0 ) *nOut = -1; |
| 1290 | return NULL; |
| 1291 | } |
| 1292 | if( nOut!=0 ) *nOut = pValue->nData; |
| 1293 | return pValue->zData; |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** Return true if variable (zVar, nVar) exists. |
| 1298 | */ |
| 1299 | int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ |
| 1300 |
M
src/th.h
+13
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -56,10 +56,23 @@ | ||
| 56 | 56 | int Th_GetVar(Th_Interp *, const char *, int); |
| 57 | 57 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 58 | 58 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 59 | 59 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 60 | 60 | |
| 61 | +/* | |
| 62 | +** If interp has a variable with the given name, its value is returned | |
| 63 | +** and its length is returned via *nOut if nOut is not NULL. If | |
| 64 | +** interp has no such var then NULL is returned without setting any | |
| 65 | +** error state and *nOut, if not NULL, is set to 0. The returned value | |
| 66 | +** is owned by the interpreter and may be invalidated the next time | |
| 67 | +** the interpreter is modified. | |
| 68 | +** | |
| 69 | +** zVarName must be NUL-terminated. | |
| 70 | +*/ | |
| 71 | +const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, | |
| 72 | + int *nOut); | |
| 73 | + | |
| 61 | 74 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 62 | 75 | |
| 63 | 76 | /* |
| 64 | 77 | ** Register new commands. |
| 65 | 78 | */ |
| 66 | 79 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -56,10 +56,23 @@ | |
| 56 | int Th_GetVar(Th_Interp *, const char *, int); |
| 57 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 58 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 59 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 60 | |
| 61 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 62 | |
| 63 | /* |
| 64 | ** Register new commands. |
| 65 | */ |
| 66 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -56,10 +56,23 @@ | |
| 56 | int Th_GetVar(Th_Interp *, const char *, int); |
| 57 | int Th_SetVar(Th_Interp *, const char *, int, const char *, int); |
| 58 | int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); |
| 59 | int Th_UnsetVar(Th_Interp *, const char *, int); |
| 60 | |
| 61 | /* |
| 62 | ** If interp has a variable with the given name, its value is returned |
| 63 | ** and its length is returned via *nOut if nOut is not NULL. If |
| 64 | ** interp has no such var then NULL is returned without setting any |
| 65 | ** error state and *nOut, if not NULL, is set to 0. The returned value |
| 66 | ** is owned by the interpreter and may be invalidated the next time |
| 67 | ** the interpreter is modified. |
| 68 | ** |
| 69 | ** zVarName must be NUL-terminated. |
| 70 | */ |
| 71 | const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName, |
| 72 | int *nOut); |
| 73 | |
| 74 | typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); |
| 75 | |
| 76 | /* |
| 77 | ** Register new commands. |
| 78 | */ |
| 79 |
+24
-24
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -290,11 +290,11 @@ | ||
| 290 | 290 | if( argc<2 || argc>3 ){ |
| 291 | 291 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 292 | 292 | } |
| 293 | 293 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 294 | 294 | if( g.thTrace ){ |
| 295 | - Th_Trace("enable_output {%.*s} -> %d<br />\n", argl[1],argv[1],enableOutput); | |
| 295 | + Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput); | |
| 296 | 296 | } |
| 297 | 297 | return rc; |
| 298 | 298 | } |
| 299 | 299 | |
| 300 | 300 | /* |
| @@ -320,11 +320,11 @@ | ||
| 320 | 320 | } |
| 321 | 321 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 322 | 322 | Th_SetResultInt(g.interp, buul); |
| 323 | 323 | if(argc>1){ |
| 324 | 324 | if( g.thTrace ){ |
| 325 | - Th_Trace("enable_htmlify {%.*s} -> %d<br />\n", | |
| 325 | + Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", | |
| 326 | 326 | argl[1],argv[1],buul); |
| 327 | 327 | } |
| 328 | 328 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 329 | 329 | if(!rc){ |
| 330 | 330 | if(buul){ |
| @@ -412,11 +412,11 @@ | ||
| 412 | 412 | */ |
| 413 | 413 | static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ |
| 414 | 414 | int savedEnable = enableOutput; |
| 415 | 415 | enableOutput = 1; |
| 416 | 416 | if( forceCgi || g.cgiOutput ){ |
| 417 | - sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0); | |
| 417 | + sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0); | |
| 418 | 418 | } |
| 419 | 419 | sendText(pOut,"ERROR: ", -1, 0); |
| 420 | 420 | sendText(pOut,(char*)z, n, 1); |
| 421 | 421 | sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); |
| 422 | 422 | enableOutput = savedEnable; |
| @@ -791,11 +791,11 @@ | ||
| 791 | 791 | Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); |
| 792 | 792 | } |
| 793 | 793 | rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); |
| 794 | 794 | } |
| 795 | 795 | if( g.thTrace ){ |
| 796 | - Th_Trace("[%s %#h] => %d<br />\n", argv[0], nCapList, zCapList, rc); | |
| 796 | + Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); | |
| 797 | 797 | Th_Free(interp, zCapList); |
| 798 | 798 | } |
| 799 | 799 | Th_SetResultInt(interp, rc); |
| 800 | 800 | return TH_OK; |
| 801 | 801 | } |
| @@ -908,11 +908,11 @@ | ||
| 908 | 908 | } |
| 909 | 909 | } |
| 910 | 910 | if( !match ) rc = 0; |
| 911 | 911 | } |
| 912 | 912 | if( g.thTrace ){ |
| 913 | - Th_Trace("[searchable %#h] => %d<br />\n", argl[1], argv[1], rc); | |
| 913 | + Th_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc); | |
| 914 | 914 | } |
| 915 | 915 | Th_SetResultInt(interp, rc); |
| 916 | 916 | return TH_OK; |
| 917 | 917 | } |
| 918 | 918 | |
| @@ -1027,11 +1027,11 @@ | ||
| 1027 | 1027 | #endif |
| 1028 | 1028 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1029 | 1029 | rc = 1; |
| 1030 | 1030 | } |
| 1031 | 1031 | if( g.thTrace ){ |
| 1032 | - Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc); | |
| 1032 | + Th_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc); | |
| 1033 | 1033 | } |
| 1034 | 1034 | Th_SetResultInt(interp, rc); |
| 1035 | 1035 | return TH_OK; |
| 1036 | 1036 | } |
| 1037 | 1037 | |
| @@ -1058,11 +1058,11 @@ | ||
| 1058 | 1058 | if( g.tcl.interp ){ |
| 1059 | 1059 | rc = 1; |
| 1060 | 1060 | } |
| 1061 | 1061 | #endif |
| 1062 | 1062 | if( g.thTrace ){ |
| 1063 | - Th_Trace("[tclReady] => %d<br />\n", rc); | |
| 1063 | + Th_Trace("[tclReady] => %d<br>\n", rc); | |
| 1064 | 1064 | } |
| 1065 | 1065 | Th_SetResultInt(interp, rc); |
| 1066 | 1066 | return TH_OK; |
| 1067 | 1067 | } |
| 1068 | 1068 | |
| @@ -1087,11 +1087,11 @@ | ||
| 1087 | 1087 | } |
| 1088 | 1088 | for(i=0; rc==0 && i<argl[1]; i++){ |
| 1089 | 1089 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1090 | 1090 | } |
| 1091 | 1091 | if( g.thTrace ){ |
| 1092 | - Th_Trace("[anycap %#h] => %d<br />\n", argl[1], argv[1], rc); | |
| 1092 | + Th_Trace("[anycap %#h] => %d<br>\n", argl[1], argv[1], rc); | |
| 1093 | 1093 | } |
| 1094 | 1094 | Th_SetResultInt(interp, rc); |
| 1095 | 1095 | return TH_OK; |
| 1096 | 1096 | } |
| 1097 | 1097 | |
| @@ -1953,17 +1953,17 @@ | ||
| 1953 | 1953 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 1954 | 1954 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 1955 | 1955 | Th_SetVar(interp, zCol, szCol, zVal, szVal); |
| 1956 | 1956 | } |
| 1957 | 1957 | if( g.thTrace ){ |
| 1958 | - Th_Trace("query_eval {<pre>%#h</pre>}<br />\n", argl[2], argv[2]); | |
| 1958 | + Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]); | |
| 1959 | 1959 | } |
| 1960 | 1960 | res = Th_Eval(interp, 0, argv[2], argl[2]); |
| 1961 | 1961 | if( g.thTrace ){ |
| 1962 | 1962 | int nTrRes; |
| 1963 | 1963 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 1964 | - Th_Trace("[query_eval] => %h {%#h}<br />\n", | |
| 1964 | + Th_Trace("[query_eval] => %h {%#h}<br>\n", | |
| 1965 | 1965 | Th_ReturnCodeName(res, 0), nTrRes, zTrRes); |
| 1966 | 1966 | } |
| 1967 | 1967 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 1968 | 1968 | } |
| 1969 | 1969 | rc = sqlite3_finalize(pStmt); |
| @@ -2013,11 +2013,11 @@ | ||
| 2013 | 2013 | }else{ |
| 2014 | 2014 | Th_SetResult(interp, 0, 0); |
| 2015 | 2015 | rc = TH_OK; |
| 2016 | 2016 | } |
| 2017 | 2017 | if( g.thTrace ){ |
| 2018 | - Th_Trace("[setting %s%#h] => %d<br />\n", strict ? "strict " : "", | |
| 2018 | + Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", | |
| 2019 | 2019 | argl[nArg], argv[nArg], rc); |
| 2020 | 2020 | } |
| 2021 | 2021 | return rc; |
| 2022 | 2022 | } |
| 2023 | 2023 | |
| @@ -2374,11 +2374,11 @@ | ||
| 2374 | 2374 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2375 | 2375 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2376 | 2376 | {0, 0, 0} |
| 2377 | 2377 | }; |
| 2378 | 2378 | if( g.thTrace ){ |
| 2379 | - Th_Trace("th1-init 0x%x => 0x%x<br />\n", g.th1Flags, flags); | |
| 2379 | + Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags); | |
| 2380 | 2380 | } |
| 2381 | 2381 | if( needConfig ){ |
| 2382 | 2382 | /* |
| 2383 | 2383 | ** This function uses several settings which may be defined in the |
| 2384 | 2384 | ** repository and/or the global configuration. Since the caller |
| @@ -2394,11 +2394,11 @@ | ||
| 2394 | 2394 | Th_Vtab *pVtab = 0; |
| 2395 | 2395 | #if defined(TH_MEMDEBUG) |
| 2396 | 2396 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 2397 | 2397 | pVtab = &vtab; |
| 2398 | 2398 | if( g.thTrace ){ |
| 2399 | - Th_Trace("th1-init MEMDEBUG ENABLED<br />\n"); | |
| 2399 | + Th_Trace("th1-init MEMDEBUG ENABLED<br>\n"); | |
| 2400 | 2400 | } |
| 2401 | 2401 | } |
| 2402 | 2402 | #endif |
| 2403 | 2403 | g.interp = Th_CreateInterp(pVtab); |
| 2404 | 2404 | created = 1; |
| @@ -2435,11 +2435,11 @@ | ||
| 2435 | 2435 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2436 | 2436 | sendError(0,zResult, nResult, 0); |
| 2437 | 2437 | } |
| 2438 | 2438 | } |
| 2439 | 2439 | if( g.thTrace ){ |
| 2440 | - Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, | |
| 2440 | + Th_Trace("th1-setup {%h} => %h<br>\n", g.th1Setup, | |
| 2441 | 2441 | Th_ReturnCodeName(rc, 0)); |
| 2442 | 2442 | } |
| 2443 | 2443 | } |
| 2444 | 2444 | g.th1Flags &= ~TH_INIT_MASK; |
| 2445 | 2445 | g.th1Flags |= (flags & TH_INIT_MASK); |
| @@ -2451,11 +2451,11 @@ | ||
| 2451 | 2451 | */ |
| 2452 | 2452 | void Th_MaybeStore(const char *zName, const char *zValue){ |
| 2453 | 2453 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2454 | 2454 | if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ |
| 2455 | 2455 | if( g.thTrace ){ |
| 2456 | - Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue); | |
| 2456 | + Th_Trace("maybe_set %h {%h}<br>\n", zName, zValue); | |
| 2457 | 2457 | } |
| 2458 | 2458 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2459 | 2459 | } |
| 2460 | 2460 | } |
| 2461 | 2461 | |
| @@ -2464,11 +2464,11 @@ | ||
| 2464 | 2464 | */ |
| 2465 | 2465 | void Th_Store(const char *zName, const char *zValue){ |
| 2466 | 2466 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2467 | 2467 | if( zValue ){ |
| 2468 | 2468 | if( g.thTrace ){ |
| 2469 | - Th_Trace("set %h {%h}<br />\n", zName, zValue); | |
| 2469 | + Th_Trace("set %h {%h}<br>\n", zName, zValue); | |
| 2470 | 2470 | } |
| 2471 | 2471 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2472 | 2472 | } |
| 2473 | 2473 | } |
| 2474 | 2474 | |
| @@ -2507,11 +2507,11 @@ | ||
| 2507 | 2507 | int i; |
| 2508 | 2508 | for(i=0; i<nList; i++){ |
| 2509 | 2509 | Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1); |
| 2510 | 2510 | } |
| 2511 | 2511 | if( g.thTrace ){ |
| 2512 | - Th_Trace("set %h {%h}<br />\n", zName, zValue); | |
| 2512 | + Th_Trace("set %h {%h}<br>\n", zName, zValue); | |
| 2513 | 2513 | } |
| 2514 | 2514 | Th_SetVar(g.interp, zName, -1, zValue, nValue); |
| 2515 | 2515 | Th_Free(g.interp, zValue); |
| 2516 | 2516 | } |
| 2517 | 2517 | } |
| @@ -2525,11 +2525,11 @@ | ||
| 2525 | 2525 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2526 | 2526 | blob_zero(&value); |
| 2527 | 2527 | blob_appendf(&value, "%d", iValue); |
| 2528 | 2528 | zValue = blob_str(&value); |
| 2529 | 2529 | if( g.thTrace ){ |
| 2530 | - Th_Trace("set %h {%h}<br />\n", zName, zValue); | |
| 2530 | + Th_Trace("set %h {%h}<br>\n", zName, zValue); | |
| 2531 | 2531 | } |
| 2532 | 2532 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2533 | 2533 | blob_reset(&value); |
| 2534 | 2534 | } |
| 2535 | 2535 | |
| @@ -2673,11 +2673,11 @@ | ||
| 2673 | 2673 | ** do not want to skip executing essential commands unless the called command |
| 2674 | 2674 | ** (i.e. "command_hook") explicitly forbids this by successfully returning |
| 2675 | 2675 | ** TH_BREAK or TH_CONTINUE. |
| 2676 | 2676 | */ |
| 2677 | 2677 | if( g.thTrace ){ |
| 2678 | - Th_Trace("[command_hook {%h}] => %h<br />\n", zName, | |
| 2678 | + Th_Trace("[command_hook {%h}] => %h<br>\n", zName, | |
| 2679 | 2679 | Th_ReturnCodeName(rc, 0)); |
| 2680 | 2680 | } |
| 2681 | 2681 | /* |
| 2682 | 2682 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2683 | 2683 | ** clean it up now. This is very important because some commands do not |
| @@ -2705,11 +2705,11 @@ | ||
| 2705 | 2705 | Th_Store("cmd_name", zName); |
| 2706 | 2706 | Th_StoreList("cmd_args", g.argv, g.argc); |
| 2707 | 2707 | Th_StoreInt("cmd_flags", cmdFlags); |
| 2708 | 2708 | rc = Th_Eval(g.interp, 0, "command_notify", -1); |
| 2709 | 2709 | if( g.thTrace ){ |
| 2710 | - Th_Trace("[command_notify {%h}] => %h<br />\n", zName, | |
| 2710 | + Th_Trace("[command_notify {%h}] => %h<br>\n", zName, | |
| 2711 | 2711 | Th_ReturnCodeName(rc, 0)); |
| 2712 | 2712 | } |
| 2713 | 2713 | /* |
| 2714 | 2714 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2715 | 2715 | ** clean it up now. This is very important because some commands do not |
| @@ -2760,11 +2760,11 @@ | ||
| 2760 | 2760 | ** do not want to skip processing essential web pages unless the called |
| 2761 | 2761 | ** command (i.e. "webpage_hook") explicitly forbids this by successfully |
| 2762 | 2762 | ** returning TH_BREAK or TH_CONTINUE. |
| 2763 | 2763 | */ |
| 2764 | 2764 | if( g.thTrace ){ |
| 2765 | - Th_Trace("[webpage_hook {%h}] => %h<br />\n", zName, | |
| 2765 | + Th_Trace("[webpage_hook {%h}] => %h<br>\n", zName, | |
| 2766 | 2766 | Th_ReturnCodeName(rc, 0)); |
| 2767 | 2767 | } |
| 2768 | 2768 | /* |
| 2769 | 2769 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2770 | 2770 | ** clean it up now. This is very important because some commands do not |
| @@ -2792,11 +2792,11 @@ | ||
| 2792 | 2792 | Th_Store("web_name", zName); |
| 2793 | 2793 | Th_StoreList("web_args", g.argv, g.argc); |
| 2794 | 2794 | Th_StoreInt("web_flags", cmdFlags); |
| 2795 | 2795 | rc = Th_Eval(g.interp, 0, "webpage_notify", -1); |
| 2796 | 2796 | if( g.thTrace ){ |
| 2797 | - Th_Trace("[webpage_notify {%h}] => %h<br />\n", zName, | |
| 2797 | + Th_Trace("[webpage_notify {%h}] => %h<br>\n", zName, | |
| 2798 | 2798 | Th_ReturnCodeName(rc, 0)); |
| 2799 | 2799 | } |
| 2800 | 2800 | /* |
| 2801 | 2801 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2802 | 2802 | ** clean it up now. This is very important because some commands do not |
| @@ -2875,17 +2875,17 @@ | ||
| 2875 | 2875 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2876 | 2876 | sendText(pOut,z, i, 0); |
| 2877 | 2877 | z += i+5; |
| 2878 | 2878 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2879 | 2879 | if( g.thTrace ){ |
| 2880 | - Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z); | |
| 2880 | + Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z); | |
| 2881 | 2881 | } |
| 2882 | 2882 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2883 | 2883 | if( g.thTrace ){ |
| 2884 | 2884 | int nTrRes; |
| 2885 | 2885 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2886 | - Th_Trace("[render_eval] => %h {%#h}<br />\n", | |
| 2886 | + Th_Trace("[render_eval] => %h {%#h}<br>\n", | |
| 2887 | 2887 | Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); |
| 2888 | 2888 | } |
| 2889 | 2889 | if( rc!=TH_OK ) break; |
| 2890 | 2890 | z += i; |
| 2891 | 2891 | if( z[0] ){ z += 6; } |
| 2892 | 2892 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -290,11 +290,11 @@ | |
| 290 | if( argc<2 || argc>3 ){ |
| 291 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 292 | } |
| 293 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 294 | if( g.thTrace ){ |
| 295 | Th_Trace("enable_output {%.*s} -> %d<br />\n", argl[1],argv[1],enableOutput); |
| 296 | } |
| 297 | return rc; |
| 298 | } |
| 299 | |
| 300 | /* |
| @@ -320,11 +320,11 @@ | |
| 320 | } |
| 321 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 322 | Th_SetResultInt(g.interp, buul); |
| 323 | if(argc>1){ |
| 324 | if( g.thTrace ){ |
| 325 | Th_Trace("enable_htmlify {%.*s} -> %d<br />\n", |
| 326 | argl[1],argv[1],buul); |
| 327 | } |
| 328 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 329 | if(!rc){ |
| 330 | if(buul){ |
| @@ -412,11 +412,11 @@ | |
| 412 | */ |
| 413 | static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ |
| 414 | int savedEnable = enableOutput; |
| 415 | enableOutput = 1; |
| 416 | if( forceCgi || g.cgiOutput ){ |
| 417 | sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0); |
| 418 | } |
| 419 | sendText(pOut,"ERROR: ", -1, 0); |
| 420 | sendText(pOut,(char*)z, n, 1); |
| 421 | sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); |
| 422 | enableOutput = savedEnable; |
| @@ -791,11 +791,11 @@ | |
| 791 | Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); |
| 792 | } |
| 793 | rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); |
| 794 | } |
| 795 | if( g.thTrace ){ |
| 796 | Th_Trace("[%s %#h] => %d<br />\n", argv[0], nCapList, zCapList, rc); |
| 797 | Th_Free(interp, zCapList); |
| 798 | } |
| 799 | Th_SetResultInt(interp, rc); |
| 800 | return TH_OK; |
| 801 | } |
| @@ -908,11 +908,11 @@ | |
| 908 | } |
| 909 | } |
| 910 | if( !match ) rc = 0; |
| 911 | } |
| 912 | if( g.thTrace ){ |
| 913 | Th_Trace("[searchable %#h] => %d<br />\n", argl[1], argv[1], rc); |
| 914 | } |
| 915 | Th_SetResultInt(interp, rc); |
| 916 | return TH_OK; |
| 917 | } |
| 918 | |
| @@ -1027,11 +1027,11 @@ | |
| 1027 | #endif |
| 1028 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1029 | rc = 1; |
| 1030 | } |
| 1031 | if( g.thTrace ){ |
| 1032 | Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc); |
| 1033 | } |
| 1034 | Th_SetResultInt(interp, rc); |
| 1035 | return TH_OK; |
| 1036 | } |
| 1037 | |
| @@ -1058,11 +1058,11 @@ | |
| 1058 | if( g.tcl.interp ){ |
| 1059 | rc = 1; |
| 1060 | } |
| 1061 | #endif |
| 1062 | if( g.thTrace ){ |
| 1063 | Th_Trace("[tclReady] => %d<br />\n", rc); |
| 1064 | } |
| 1065 | Th_SetResultInt(interp, rc); |
| 1066 | return TH_OK; |
| 1067 | } |
| 1068 | |
| @@ -1087,11 +1087,11 @@ | |
| 1087 | } |
| 1088 | for(i=0; rc==0 && i<argl[1]; i++){ |
| 1089 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1090 | } |
| 1091 | if( g.thTrace ){ |
| 1092 | Th_Trace("[anycap %#h] => %d<br />\n", argl[1], argv[1], rc); |
| 1093 | } |
| 1094 | Th_SetResultInt(interp, rc); |
| 1095 | return TH_OK; |
| 1096 | } |
| 1097 | |
| @@ -1953,17 +1953,17 @@ | |
| 1953 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 1954 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 1955 | Th_SetVar(interp, zCol, szCol, zVal, szVal); |
| 1956 | } |
| 1957 | if( g.thTrace ){ |
| 1958 | Th_Trace("query_eval {<pre>%#h</pre>}<br />\n", argl[2], argv[2]); |
| 1959 | } |
| 1960 | res = Th_Eval(interp, 0, argv[2], argl[2]); |
| 1961 | if( g.thTrace ){ |
| 1962 | int nTrRes; |
| 1963 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 1964 | Th_Trace("[query_eval] => %h {%#h}<br />\n", |
| 1965 | Th_ReturnCodeName(res, 0), nTrRes, zTrRes); |
| 1966 | } |
| 1967 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 1968 | } |
| 1969 | rc = sqlite3_finalize(pStmt); |
| @@ -2013,11 +2013,11 @@ | |
| 2013 | }else{ |
| 2014 | Th_SetResult(interp, 0, 0); |
| 2015 | rc = TH_OK; |
| 2016 | } |
| 2017 | if( g.thTrace ){ |
| 2018 | Th_Trace("[setting %s%#h] => %d<br />\n", strict ? "strict " : "", |
| 2019 | argl[nArg], argv[nArg], rc); |
| 2020 | } |
| 2021 | return rc; |
| 2022 | } |
| 2023 | |
| @@ -2374,11 +2374,11 @@ | |
| 2374 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2375 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2376 | {0, 0, 0} |
| 2377 | }; |
| 2378 | if( g.thTrace ){ |
| 2379 | Th_Trace("th1-init 0x%x => 0x%x<br />\n", g.th1Flags, flags); |
| 2380 | } |
| 2381 | if( needConfig ){ |
| 2382 | /* |
| 2383 | ** This function uses several settings which may be defined in the |
| 2384 | ** repository and/or the global configuration. Since the caller |
| @@ -2394,11 +2394,11 @@ | |
| 2394 | Th_Vtab *pVtab = 0; |
| 2395 | #if defined(TH_MEMDEBUG) |
| 2396 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 2397 | pVtab = &vtab; |
| 2398 | if( g.thTrace ){ |
| 2399 | Th_Trace("th1-init MEMDEBUG ENABLED<br />\n"); |
| 2400 | } |
| 2401 | } |
| 2402 | #endif |
| 2403 | g.interp = Th_CreateInterp(pVtab); |
| 2404 | created = 1; |
| @@ -2435,11 +2435,11 @@ | |
| 2435 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2436 | sendError(0,zResult, nResult, 0); |
| 2437 | } |
| 2438 | } |
| 2439 | if( g.thTrace ){ |
| 2440 | Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, |
| 2441 | Th_ReturnCodeName(rc, 0)); |
| 2442 | } |
| 2443 | } |
| 2444 | g.th1Flags &= ~TH_INIT_MASK; |
| 2445 | g.th1Flags |= (flags & TH_INIT_MASK); |
| @@ -2451,11 +2451,11 @@ | |
| 2451 | */ |
| 2452 | void Th_MaybeStore(const char *zName, const char *zValue){ |
| 2453 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2454 | if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ |
| 2455 | if( g.thTrace ){ |
| 2456 | Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue); |
| 2457 | } |
| 2458 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2459 | } |
| 2460 | } |
| 2461 | |
| @@ -2464,11 +2464,11 @@ | |
| 2464 | */ |
| 2465 | void Th_Store(const char *zName, const char *zValue){ |
| 2466 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2467 | if( zValue ){ |
| 2468 | if( g.thTrace ){ |
| 2469 | Th_Trace("set %h {%h}<br />\n", zName, zValue); |
| 2470 | } |
| 2471 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2472 | } |
| 2473 | } |
| 2474 | |
| @@ -2507,11 +2507,11 @@ | |
| 2507 | int i; |
| 2508 | for(i=0; i<nList; i++){ |
| 2509 | Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1); |
| 2510 | } |
| 2511 | if( g.thTrace ){ |
| 2512 | Th_Trace("set %h {%h}<br />\n", zName, zValue); |
| 2513 | } |
| 2514 | Th_SetVar(g.interp, zName, -1, zValue, nValue); |
| 2515 | Th_Free(g.interp, zValue); |
| 2516 | } |
| 2517 | } |
| @@ -2525,11 +2525,11 @@ | |
| 2525 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2526 | blob_zero(&value); |
| 2527 | blob_appendf(&value, "%d", iValue); |
| 2528 | zValue = blob_str(&value); |
| 2529 | if( g.thTrace ){ |
| 2530 | Th_Trace("set %h {%h}<br />\n", zName, zValue); |
| 2531 | } |
| 2532 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2533 | blob_reset(&value); |
| 2534 | } |
| 2535 | |
| @@ -2673,11 +2673,11 @@ | |
| 2673 | ** do not want to skip executing essential commands unless the called command |
| 2674 | ** (i.e. "command_hook") explicitly forbids this by successfully returning |
| 2675 | ** TH_BREAK or TH_CONTINUE. |
| 2676 | */ |
| 2677 | if( g.thTrace ){ |
| 2678 | Th_Trace("[command_hook {%h}] => %h<br />\n", zName, |
| 2679 | Th_ReturnCodeName(rc, 0)); |
| 2680 | } |
| 2681 | /* |
| 2682 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2683 | ** clean it up now. This is very important because some commands do not |
| @@ -2705,11 +2705,11 @@ | |
| 2705 | Th_Store("cmd_name", zName); |
| 2706 | Th_StoreList("cmd_args", g.argv, g.argc); |
| 2707 | Th_StoreInt("cmd_flags", cmdFlags); |
| 2708 | rc = Th_Eval(g.interp, 0, "command_notify", -1); |
| 2709 | if( g.thTrace ){ |
| 2710 | Th_Trace("[command_notify {%h}] => %h<br />\n", zName, |
| 2711 | Th_ReturnCodeName(rc, 0)); |
| 2712 | } |
| 2713 | /* |
| 2714 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2715 | ** clean it up now. This is very important because some commands do not |
| @@ -2760,11 +2760,11 @@ | |
| 2760 | ** do not want to skip processing essential web pages unless the called |
| 2761 | ** command (i.e. "webpage_hook") explicitly forbids this by successfully |
| 2762 | ** returning TH_BREAK or TH_CONTINUE. |
| 2763 | */ |
| 2764 | if( g.thTrace ){ |
| 2765 | Th_Trace("[webpage_hook {%h}] => %h<br />\n", zName, |
| 2766 | Th_ReturnCodeName(rc, 0)); |
| 2767 | } |
| 2768 | /* |
| 2769 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2770 | ** clean it up now. This is very important because some commands do not |
| @@ -2792,11 +2792,11 @@ | |
| 2792 | Th_Store("web_name", zName); |
| 2793 | Th_StoreList("web_args", g.argv, g.argc); |
| 2794 | Th_StoreInt("web_flags", cmdFlags); |
| 2795 | rc = Th_Eval(g.interp, 0, "webpage_notify", -1); |
| 2796 | if( g.thTrace ){ |
| 2797 | Th_Trace("[webpage_notify {%h}] => %h<br />\n", zName, |
| 2798 | Th_ReturnCodeName(rc, 0)); |
| 2799 | } |
| 2800 | /* |
| 2801 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2802 | ** clean it up now. This is very important because some commands do not |
| @@ -2875,17 +2875,17 @@ | |
| 2875 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2876 | sendText(pOut,z, i, 0); |
| 2877 | z += i+5; |
| 2878 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2879 | if( g.thTrace ){ |
| 2880 | Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z); |
| 2881 | } |
| 2882 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2883 | if( g.thTrace ){ |
| 2884 | int nTrRes; |
| 2885 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2886 | Th_Trace("[render_eval] => %h {%#h}<br />\n", |
| 2887 | Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); |
| 2888 | } |
| 2889 | if( rc!=TH_OK ) break; |
| 2890 | z += i; |
| 2891 | if( z[0] ){ z += 6; } |
| 2892 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -290,11 +290,11 @@ | |
| 290 | if( argc<2 || argc>3 ){ |
| 291 | return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); |
| 292 | } |
| 293 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); |
| 294 | if( g.thTrace ){ |
| 295 | Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput); |
| 296 | } |
| 297 | return rc; |
| 298 | } |
| 299 | |
| 300 | /* |
| @@ -320,11 +320,11 @@ | |
| 320 | } |
| 321 | buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1; |
| 322 | Th_SetResultInt(g.interp, buul); |
| 323 | if(argc>1){ |
| 324 | if( g.thTrace ){ |
| 325 | Th_Trace("enable_htmlify {%.*s} -> %d<br>\n", |
| 326 | argl[1],argv[1],buul); |
| 327 | } |
| 328 | rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul); |
| 329 | if(!rc){ |
| 330 | if(buul){ |
| @@ -412,11 +412,11 @@ | |
| 412 | */ |
| 413 | static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ |
| 414 | int savedEnable = enableOutput; |
| 415 | enableOutput = 1; |
| 416 | if( forceCgi || g.cgiOutput ){ |
| 417 | sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0); |
| 418 | } |
| 419 | sendText(pOut,"ERROR: ", -1, 0); |
| 420 | sendText(pOut,(char*)z, n, 1); |
| 421 | sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); |
| 422 | enableOutput = savedEnable; |
| @@ -791,11 +791,11 @@ | |
| 791 | Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]); |
| 792 | } |
| 793 | rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); |
| 794 | } |
| 795 | if( g.thTrace ){ |
| 796 | Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc); |
| 797 | Th_Free(interp, zCapList); |
| 798 | } |
| 799 | Th_SetResultInt(interp, rc); |
| 800 | return TH_OK; |
| 801 | } |
| @@ -908,11 +908,11 @@ | |
| 908 | } |
| 909 | } |
| 910 | if( !match ) rc = 0; |
| 911 | } |
| 912 | if( g.thTrace ){ |
| 913 | Th_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc); |
| 914 | } |
| 915 | Th_SetResultInt(interp, rc); |
| 916 | return TH_OK; |
| 917 | } |
| 918 | |
| @@ -1027,11 +1027,11 @@ | |
| 1027 | #endif |
| 1028 | else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ |
| 1029 | rc = 1; |
| 1030 | } |
| 1031 | if( g.thTrace ){ |
| 1032 | Th_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc); |
| 1033 | } |
| 1034 | Th_SetResultInt(interp, rc); |
| 1035 | return TH_OK; |
| 1036 | } |
| 1037 | |
| @@ -1058,11 +1058,11 @@ | |
| 1058 | if( g.tcl.interp ){ |
| 1059 | rc = 1; |
| 1060 | } |
| 1061 | #endif |
| 1062 | if( g.thTrace ){ |
| 1063 | Th_Trace("[tclReady] => %d<br>\n", rc); |
| 1064 | } |
| 1065 | Th_SetResultInt(interp, rc); |
| 1066 | return TH_OK; |
| 1067 | } |
| 1068 | |
| @@ -1087,11 +1087,11 @@ | |
| 1087 | } |
| 1088 | for(i=0; rc==0 && i<argl[1]; i++){ |
| 1089 | rc = login_has_capability((char*)&argv[1][i],1,0); |
| 1090 | } |
| 1091 | if( g.thTrace ){ |
| 1092 | Th_Trace("[anycap %#h] => %d<br>\n", argl[1], argv[1], rc); |
| 1093 | } |
| 1094 | Th_SetResultInt(interp, rc); |
| 1095 | return TH_OK; |
| 1096 | } |
| 1097 | |
| @@ -1953,17 +1953,17 @@ | |
| 1953 | const char *zVal = (const char*)sqlite3_column_text(pStmt, i); |
| 1954 | int szVal = sqlite3_column_bytes(pStmt, i); |
| 1955 | Th_SetVar(interp, zCol, szCol, zVal, szVal); |
| 1956 | } |
| 1957 | if( g.thTrace ){ |
| 1958 | Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]); |
| 1959 | } |
| 1960 | res = Th_Eval(interp, 0, argv[2], argl[2]); |
| 1961 | if( g.thTrace ){ |
| 1962 | int nTrRes; |
| 1963 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 1964 | Th_Trace("[query_eval] => %h {%#h}<br>\n", |
| 1965 | Th_ReturnCodeName(res, 0), nTrRes, zTrRes); |
| 1966 | } |
| 1967 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; |
| 1968 | } |
| 1969 | rc = sqlite3_finalize(pStmt); |
| @@ -2013,11 +2013,11 @@ | |
| 2013 | }else{ |
| 2014 | Th_SetResult(interp, 0, 0); |
| 2015 | rc = TH_OK; |
| 2016 | } |
| 2017 | if( g.thTrace ){ |
| 2018 | Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "", |
| 2019 | argl[nArg], argv[nArg], rc); |
| 2020 | } |
| 2021 | return rc; |
| 2022 | } |
| 2023 | |
| @@ -2374,11 +2374,11 @@ | |
| 2374 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2375 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2376 | {0, 0, 0} |
| 2377 | }; |
| 2378 | if( g.thTrace ){ |
| 2379 | Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags); |
| 2380 | } |
| 2381 | if( needConfig ){ |
| 2382 | /* |
| 2383 | ** This function uses several settings which may be defined in the |
| 2384 | ** repository and/or the global configuration. Since the caller |
| @@ -2394,11 +2394,11 @@ | |
| 2394 | Th_Vtab *pVtab = 0; |
| 2395 | #if defined(TH_MEMDEBUG) |
| 2396 | if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ |
| 2397 | pVtab = &vtab; |
| 2398 | if( g.thTrace ){ |
| 2399 | Th_Trace("th1-init MEMDEBUG ENABLED<br>\n"); |
| 2400 | } |
| 2401 | } |
| 2402 | #endif |
| 2403 | g.interp = Th_CreateInterp(pVtab); |
| 2404 | created = 1; |
| @@ -2435,11 +2435,11 @@ | |
| 2435 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2436 | sendError(0,zResult, nResult, 0); |
| 2437 | } |
| 2438 | } |
| 2439 | if( g.thTrace ){ |
| 2440 | Th_Trace("th1-setup {%h} => %h<br>\n", g.th1Setup, |
| 2441 | Th_ReturnCodeName(rc, 0)); |
| 2442 | } |
| 2443 | } |
| 2444 | g.th1Flags &= ~TH_INIT_MASK; |
| 2445 | g.th1Flags |= (flags & TH_INIT_MASK); |
| @@ -2451,11 +2451,11 @@ | |
| 2451 | */ |
| 2452 | void Th_MaybeStore(const char *zName, const char *zValue){ |
| 2453 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2454 | if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ |
| 2455 | if( g.thTrace ){ |
| 2456 | Th_Trace("maybe_set %h {%h}<br>\n", zName, zValue); |
| 2457 | } |
| 2458 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2459 | } |
| 2460 | } |
| 2461 | |
| @@ -2464,11 +2464,11 @@ | |
| 2464 | */ |
| 2465 | void Th_Store(const char *zName, const char *zValue){ |
| 2466 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2467 | if( zValue ){ |
| 2468 | if( g.thTrace ){ |
| 2469 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2470 | } |
| 2471 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2472 | } |
| 2473 | } |
| 2474 | |
| @@ -2507,11 +2507,11 @@ | |
| 2507 | int i; |
| 2508 | for(i=0; i<nList; i++){ |
| 2509 | Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1); |
| 2510 | } |
| 2511 | if( g.thTrace ){ |
| 2512 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2513 | } |
| 2514 | Th_SetVar(g.interp, zName, -1, zValue, nValue); |
| 2515 | Th_Free(g.interp, zValue); |
| 2516 | } |
| 2517 | } |
| @@ -2525,11 +2525,11 @@ | |
| 2525 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2526 | blob_zero(&value); |
| 2527 | blob_appendf(&value, "%d", iValue); |
| 2528 | zValue = blob_str(&value); |
| 2529 | if( g.thTrace ){ |
| 2530 | Th_Trace("set %h {%h}<br>\n", zName, zValue); |
| 2531 | } |
| 2532 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2533 | blob_reset(&value); |
| 2534 | } |
| 2535 | |
| @@ -2673,11 +2673,11 @@ | |
| 2673 | ** do not want to skip executing essential commands unless the called command |
| 2674 | ** (i.e. "command_hook") explicitly forbids this by successfully returning |
| 2675 | ** TH_BREAK or TH_CONTINUE. |
| 2676 | */ |
| 2677 | if( g.thTrace ){ |
| 2678 | Th_Trace("[command_hook {%h}] => %h<br>\n", zName, |
| 2679 | Th_ReturnCodeName(rc, 0)); |
| 2680 | } |
| 2681 | /* |
| 2682 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2683 | ** clean it up now. This is very important because some commands do not |
| @@ -2705,11 +2705,11 @@ | |
| 2705 | Th_Store("cmd_name", zName); |
| 2706 | Th_StoreList("cmd_args", g.argv, g.argc); |
| 2707 | Th_StoreInt("cmd_flags", cmdFlags); |
| 2708 | rc = Th_Eval(g.interp, 0, "command_notify", -1); |
| 2709 | if( g.thTrace ){ |
| 2710 | Th_Trace("[command_notify {%h}] => %h<br>\n", zName, |
| 2711 | Th_ReturnCodeName(rc, 0)); |
| 2712 | } |
| 2713 | /* |
| 2714 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2715 | ** clean it up now. This is very important because some commands do not |
| @@ -2760,11 +2760,11 @@ | |
| 2760 | ** do not want to skip processing essential web pages unless the called |
| 2761 | ** command (i.e. "webpage_hook") explicitly forbids this by successfully |
| 2762 | ** returning TH_BREAK or TH_CONTINUE. |
| 2763 | */ |
| 2764 | if( g.thTrace ){ |
| 2765 | Th_Trace("[webpage_hook {%h}] => %h<br>\n", zName, |
| 2766 | Th_ReturnCodeName(rc, 0)); |
| 2767 | } |
| 2768 | /* |
| 2769 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2770 | ** clean it up now. This is very important because some commands do not |
| @@ -2792,11 +2792,11 @@ | |
| 2792 | Th_Store("web_name", zName); |
| 2793 | Th_StoreList("web_args", g.argv, g.argc); |
| 2794 | Th_StoreInt("web_flags", cmdFlags); |
| 2795 | rc = Th_Eval(g.interp, 0, "webpage_notify", -1); |
| 2796 | if( g.thTrace ){ |
| 2797 | Th_Trace("[webpage_notify {%h}] => %h<br>\n", zName, |
| 2798 | Th_ReturnCodeName(rc, 0)); |
| 2799 | } |
| 2800 | /* |
| 2801 | ** Does our call to Th_FossilInit() result in opening a database? If so, |
| 2802 | ** clean it up now. This is very important because some commands do not |
| @@ -2875,17 +2875,17 @@ | |
| 2875 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2876 | sendText(pOut,z, i, 0); |
| 2877 | z += i+5; |
| 2878 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2879 | if( g.thTrace ){ |
| 2880 | Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z); |
| 2881 | } |
| 2882 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| 2883 | if( g.thTrace ){ |
| 2884 | int nTrRes; |
| 2885 | char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes); |
| 2886 | Th_Trace("[render_eval] => %h {%#h}<br>\n", |
| 2887 | Th_ReturnCodeName(rc, 0), nTrRes, zTrRes); |
| 2888 | } |
| 2889 | if( rc!=TH_OK ) break; |
| 2890 | z += i; |
| 2891 | if( z[0] ){ z += 6; } |
| 2892 |
+43
-10
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -144,10 +144,28 @@ | ||
| 144 | 144 | db_bind_int(&q, "$rid", rid); |
| 145 | 145 | res = db_step(&q)==SQLITE_ROW; |
| 146 | 146 | db_reset(&q); |
| 147 | 147 | return res; |
| 148 | 148 | } |
| 149 | + | |
| 150 | +/* | |
| 151 | +** Return the text of the unformatted | |
| 152 | +** forum post given by the RID in the argument. | |
| 153 | +*/ | |
| 154 | +static void forum_post_content_function( | |
| 155 | + sqlite3_context *context, | |
| 156 | + int argc, | |
| 157 | + sqlite3_value **argv | |
| 158 | +){ | |
| 159 | + int rid = sqlite3_value_int(argv[0]); | |
| 160 | + Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0); | |
| 161 | + if( pPost ){ | |
| 162 | + sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT); | |
| 163 | + manifest_destroy(pPost); | |
| 164 | + } | |
| 165 | +} | |
| 166 | + | |
| 149 | 167 | |
| 150 | 168 | /* |
| 151 | 169 | ** Output a timeline in the web format given a query. The query |
| 152 | 170 | ** should return these columns: |
| 153 | 171 | ** |
| @@ -278,11 +296,11 @@ | ||
| 278 | 296 | @ </td></tr> |
| 279 | 297 | pendingEndTr = 0; |
| 280 | 298 | } |
| 281 | 299 | if( fossil_strcmp(zType,"div")==0 ){ |
| 282 | 300 | if( !prevWasDivider ){ |
| 283 | - @ <tr><td colspan="3"><hr class="timelineMarker" /></td></tr> | |
| 301 | + @ <tr><td colspan="3"><hr class="timelineMarker"></td></tr> | |
| 284 | 302 | } |
| 285 | 303 | prevWasDivider = 1; |
| 286 | 304 | continue; |
| 287 | 305 | } |
| 288 | 306 | prevWasDivider = 0; |
| @@ -1619,10 +1637,11 @@ | ||
| 1619 | 1637 | ** to see all changes for the current year. |
| 1620 | 1638 | ** days=N Show events over the previous N days |
| 1621 | 1639 | ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, |
| 1622 | 1640 | ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". |
| 1623 | 1641 | ** bisect Show the check-ins that are in the current bisect |
| 1642 | +** oldestfirst Show events oldest first. | |
| 1624 | 1643 | ** showid Show RIDs |
| 1625 | 1644 | ** showsql Show the SQL text |
| 1626 | 1645 | ** |
| 1627 | 1646 | ** p= and d= can appear individually or together. If either p= or d= |
| 1628 | 1647 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -2554,21 +2573,31 @@ | ||
| 2554 | 2573 | } |
| 2555 | 2574 | if( zUser ){ |
| 2556 | 2575 | int n = db_int(0,"SELECT count(*) FROM event" |
| 2557 | 2576 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 2558 | 2577 | if( n<=nEntry ){ |
| 2559 | - zCirca = zBefore = zAfter = 0; | |
| 2560 | 2578 | nEntry = -1; |
| 2561 | 2579 | } |
| 2562 | 2580 | blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)", |
| 2563 | 2581 | zUser, zUser); |
| 2564 | 2582 | zThisUser = zUser; |
| 2565 | 2583 | } |
| 2566 | 2584 | if( zSearch ){ |
| 2567 | - blob_append_sql(&cond, | |
| 2568 | - " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", | |
| 2569 | - zSearch, zSearch); | |
| 2585 | + if( tmFlags & TIMELINE_FORUMTXT ){ | |
| 2586 | + sqlite3_create_function(g.db, "forum_post_content", 1, SQLITE_UTF8, | |
| 2587 | + 0, forum_post_content_function, 0, 0); | |
| 2588 | + blob_append_sql(&cond, | |
| 2589 | + " AND (event.comment LIKE '%%%q%%'" | |
| 2590 | + " OR event.brief LIKE '%%%q%%'" | |
| 2591 | + " OR (event.type=='f' AND" | |
| 2592 | + " forum_post_content(event.objid) LIKE '%%%q%%'))", | |
| 2593 | + zSearch, zSearch, zSearch); | |
| 2594 | + }else{ | |
| 2595 | + blob_append_sql(&cond, | |
| 2596 | + " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", | |
| 2597 | + zSearch, zSearch); | |
| 2598 | + } | |
| 2570 | 2599 | } |
| 2571 | 2600 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore); |
| 2572 | 2601 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter); |
| 2573 | 2602 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2574 | 2603 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| @@ -2682,19 +2711,19 @@ | ||
| 2682 | 2711 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2683 | 2712 | } |
| 2684 | 2713 | addFileGlobDescription(zChng, &desc); |
| 2685 | 2714 | if( rAfter>0.0 ){ |
| 2686 | 2715 | if( rBefore>0.0 ){ |
| 2687 | - blob_appendf(&desc, " occurring between %h and %h.<br />", | |
| 2716 | + blob_appendf(&desc, " occurring between %h and %h.<br>", | |
| 2688 | 2717 | zAfter, zBefore); |
| 2689 | 2718 | }else{ |
| 2690 | - blob_appendf(&desc, " occurring on or after %h.<br />", zAfter); | |
| 2719 | + blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); | |
| 2691 | 2720 | } |
| 2692 | 2721 | }else if( rBefore>0.0 ){ |
| 2693 | - blob_appendf(&desc, " occurring on or before %h.<br />", zBefore); | |
| 2722 | + blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); | |
| 2694 | 2723 | }else if( rCirca>0.0 ){ |
| 2695 | - blob_appendf(&desc, " occurring around %h.<br />", zCirca); | |
| 2724 | + blob_appendf(&desc, " occurring around %h.<br>", zCirca); | |
| 2696 | 2725 | } |
| 2697 | 2726 | if( zSearch ){ |
| 2698 | 2727 | blob_appendf(&desc, " matching \"%h\"", zSearch); |
| 2699 | 2728 | } |
| 2700 | 2729 | if( g.perm.Hyperlink ){ |
| @@ -2767,11 +2796,15 @@ | ||
| 2767 | 2796 | if( useDividers && zMark && zMark[0] ){ |
| 2768 | 2797 | double r = symbolic_name_to_mtime(zMark, 0); |
| 2769 | 2798 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 2770 | 2799 | } |
| 2771 | 2800 | blob_zero(&sql); |
| 2772 | - db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); | |
| 2801 | + if( PB("oldestfirst") ){ | |
| 2802 | + db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); | |
| 2803 | + }else{ | |
| 2804 | + db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); | |
| 2805 | + } | |
| 2773 | 2806 | if( fossil_islower(desc.aData[0]) ){ |
| 2774 | 2807 | desc.aData[0] = fossil_toupper(desc.aData[0]); |
| 2775 | 2808 | } |
| 2776 | 2809 | if( zBrName ){ |
| 2777 | 2810 | if( !PB("nowiki") |
| 2778 | 2811 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -144,10 +144,28 @@ | |
| 144 | db_bind_int(&q, "$rid", rid); |
| 145 | res = db_step(&q)==SQLITE_ROW; |
| 146 | db_reset(&q); |
| 147 | return res; |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Output a timeline in the web format given a query. The query |
| 152 | ** should return these columns: |
| 153 | ** |
| @@ -278,11 +296,11 @@ | |
| 278 | @ </td></tr> |
| 279 | pendingEndTr = 0; |
| 280 | } |
| 281 | if( fossil_strcmp(zType,"div")==0 ){ |
| 282 | if( !prevWasDivider ){ |
| 283 | @ <tr><td colspan="3"><hr class="timelineMarker" /></td></tr> |
| 284 | } |
| 285 | prevWasDivider = 1; |
| 286 | continue; |
| 287 | } |
| 288 | prevWasDivider = 0; |
| @@ -1619,10 +1637,11 @@ | |
| 1619 | ** to see all changes for the current year. |
| 1620 | ** days=N Show events over the previous N days |
| 1621 | ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, |
| 1622 | ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". |
| 1623 | ** bisect Show the check-ins that are in the current bisect |
| 1624 | ** showid Show RIDs |
| 1625 | ** showsql Show the SQL text |
| 1626 | ** |
| 1627 | ** p= and d= can appear individually or together. If either p= or d= |
| 1628 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -2554,21 +2573,31 @@ | |
| 2554 | } |
| 2555 | if( zUser ){ |
| 2556 | int n = db_int(0,"SELECT count(*) FROM event" |
| 2557 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 2558 | if( n<=nEntry ){ |
| 2559 | zCirca = zBefore = zAfter = 0; |
| 2560 | nEntry = -1; |
| 2561 | } |
| 2562 | blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)", |
| 2563 | zUser, zUser); |
| 2564 | zThisUser = zUser; |
| 2565 | } |
| 2566 | if( zSearch ){ |
| 2567 | blob_append_sql(&cond, |
| 2568 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2569 | zSearch, zSearch); |
| 2570 | } |
| 2571 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore); |
| 2572 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter); |
| 2573 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2574 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| @@ -2682,19 +2711,19 @@ | |
| 2682 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2683 | } |
| 2684 | addFileGlobDescription(zChng, &desc); |
| 2685 | if( rAfter>0.0 ){ |
| 2686 | if( rBefore>0.0 ){ |
| 2687 | blob_appendf(&desc, " occurring between %h and %h.<br />", |
| 2688 | zAfter, zBefore); |
| 2689 | }else{ |
| 2690 | blob_appendf(&desc, " occurring on or after %h.<br />", zAfter); |
| 2691 | } |
| 2692 | }else if( rBefore>0.0 ){ |
| 2693 | blob_appendf(&desc, " occurring on or before %h.<br />", zBefore); |
| 2694 | }else if( rCirca>0.0 ){ |
| 2695 | blob_appendf(&desc, " occurring around %h.<br />", zCirca); |
| 2696 | } |
| 2697 | if( zSearch ){ |
| 2698 | blob_appendf(&desc, " matching \"%h\"", zSearch); |
| 2699 | } |
| 2700 | if( g.perm.Hyperlink ){ |
| @@ -2767,11 +2796,15 @@ | |
| 2767 | if( useDividers && zMark && zMark[0] ){ |
| 2768 | double r = symbolic_name_to_mtime(zMark, 0); |
| 2769 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 2770 | } |
| 2771 | blob_zero(&sql); |
| 2772 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 2773 | if( fossil_islower(desc.aData[0]) ){ |
| 2774 | desc.aData[0] = fossil_toupper(desc.aData[0]); |
| 2775 | } |
| 2776 | if( zBrName ){ |
| 2777 | if( !PB("nowiki") |
| 2778 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -144,10 +144,28 @@ | |
| 144 | db_bind_int(&q, "$rid", rid); |
| 145 | res = db_step(&q)==SQLITE_ROW; |
| 146 | db_reset(&q); |
| 147 | return res; |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Return the text of the unformatted |
| 152 | ** forum post given by the RID in the argument. |
| 153 | */ |
| 154 | static void forum_post_content_function( |
| 155 | sqlite3_context *context, |
| 156 | int argc, |
| 157 | sqlite3_value **argv |
| 158 | ){ |
| 159 | int rid = sqlite3_value_int(argv[0]); |
| 160 | Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0); |
| 161 | if( pPost ){ |
| 162 | sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT); |
| 163 | manifest_destroy(pPost); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | |
| 168 | /* |
| 169 | ** Output a timeline in the web format given a query. The query |
| 170 | ** should return these columns: |
| 171 | ** |
| @@ -278,11 +296,11 @@ | |
| 296 | @ </td></tr> |
| 297 | pendingEndTr = 0; |
| 298 | } |
| 299 | if( fossil_strcmp(zType,"div")==0 ){ |
| 300 | if( !prevWasDivider ){ |
| 301 | @ <tr><td colspan="3"><hr class="timelineMarker"></td></tr> |
| 302 | } |
| 303 | prevWasDivider = 1; |
| 304 | continue; |
| 305 | } |
| 306 | prevWasDivider = 0; |
| @@ -1619,10 +1637,11 @@ | |
| 1637 | ** to see all changes for the current year. |
| 1638 | ** days=N Show events over the previous N days |
| 1639 | ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, |
| 1640 | ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". |
| 1641 | ** bisect Show the check-ins that are in the current bisect |
| 1642 | ** oldestfirst Show events oldest first. |
| 1643 | ** showid Show RIDs |
| 1644 | ** showsql Show the SQL text |
| 1645 | ** |
| 1646 | ** p= and d= can appear individually or together. If either p= or d= |
| 1647 | ** appear, then u=, y=, a=, and b= are ignored. |
| @@ -2554,21 +2573,31 @@ | |
| 2573 | } |
| 2574 | if( zUser ){ |
| 2575 | int n = db_int(0,"SELECT count(*) FROM event" |
| 2576 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 2577 | if( n<=nEntry ){ |
| 2578 | nEntry = -1; |
| 2579 | } |
| 2580 | blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)", |
| 2581 | zUser, zUser); |
| 2582 | zThisUser = zUser; |
| 2583 | } |
| 2584 | if( zSearch ){ |
| 2585 | if( tmFlags & TIMELINE_FORUMTXT ){ |
| 2586 | sqlite3_create_function(g.db, "forum_post_content", 1, SQLITE_UTF8, |
| 2587 | 0, forum_post_content_function, 0, 0); |
| 2588 | blob_append_sql(&cond, |
| 2589 | " AND (event.comment LIKE '%%%q%%'" |
| 2590 | " OR event.brief LIKE '%%%q%%'" |
| 2591 | " OR (event.type=='f' AND" |
| 2592 | " forum_post_content(event.objid) LIKE '%%%q%%'))", |
| 2593 | zSearch, zSearch, zSearch); |
| 2594 | }else{ |
| 2595 | blob_append_sql(&cond, |
| 2596 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2597 | zSearch, zSearch); |
| 2598 | } |
| 2599 | } |
| 2600 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore); |
| 2601 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter); |
| 2602 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2603 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| @@ -2682,19 +2711,19 @@ | |
| 2711 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2712 | } |
| 2713 | addFileGlobDescription(zChng, &desc); |
| 2714 | if( rAfter>0.0 ){ |
| 2715 | if( rBefore>0.0 ){ |
| 2716 | blob_appendf(&desc, " occurring between %h and %h.<br>", |
| 2717 | zAfter, zBefore); |
| 2718 | }else{ |
| 2719 | blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); |
| 2720 | } |
| 2721 | }else if( rBefore>0.0 ){ |
| 2722 | blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); |
| 2723 | }else if( rCirca>0.0 ){ |
| 2724 | blob_appendf(&desc, " occurring around %h.<br>", zCirca); |
| 2725 | } |
| 2726 | if( zSearch ){ |
| 2727 | blob_appendf(&desc, " matching \"%h\"", zSearch); |
| 2728 | } |
| 2729 | if( g.perm.Hyperlink ){ |
| @@ -2767,11 +2796,15 @@ | |
| 2796 | if( useDividers && zMark && zMark[0] ){ |
| 2797 | double r = symbolic_name_to_mtime(zMark, 0); |
| 2798 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 2799 | } |
| 2800 | blob_zero(&sql); |
| 2801 | if( PB("oldestfirst") ){ |
| 2802 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| 2803 | }else{ |
| 2804 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 2805 | } |
| 2806 | if( fossil_islower(desc.aData[0]) ){ |
| 2807 | desc.aData[0] = fossil_toupper(desc.aData[0]); |
| 2808 | } |
| 2809 | if( zBrName ){ |
| 2810 | if( !PB("nowiki") |
| 2811 |
+23
-18
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -764,27 +764,27 @@ | ||
| 764 | 764 | } |
| 765 | 765 | } |
| 766 | 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | 768 | } |
| 769 | - if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); | |
| 769 | + if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); | |
| 770 | 770 | ticket_init(); |
| 771 | 771 | initializeVariablesFromCGI(); |
| 772 | 772 | getAllTicketFields(); |
| 773 | 773 | initializeVariablesFromDb(); |
| 774 | 774 | zScript = ticket_viewpage_code(); |
| 775 | 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | - if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); | |
| 776 | + if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); | |
| 777 | 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | 778 | Th_Render(zScript); |
| 779 | - if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); | |
| 779 | + if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); | |
| 780 | 780 | |
| 781 | 781 | zFullName = db_text(0, |
| 782 | 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | 784 | if( zFullName ){ |
| 785 | - attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>"); | |
| 785 | + attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); | |
| 786 | 786 | } |
| 787 | 787 | |
| 788 | 788 | style_finish_page(); |
| 789 | 789 | } |
| 790 | 790 | |
| @@ -807,11 +807,11 @@ | ||
| 807 | 807 | |
| 808 | 808 | if( argc!=3 ){ |
| 809 | 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | 810 | } |
| 811 | 811 | if( g.thTrace ){ |
| 812 | - Th_Trace("append_field %#h {%#h}<br />\n", | |
| 812 | + Th_Trace("append_field %#h {%#h}<br>\n", | |
| 813 | 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | 814 | } |
| 815 | 815 | for(idx=0; idx<nField; idx++){ |
| 816 | 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | ||
| 967 | 967 | @ <div style="color:blue"> |
| 968 | 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | 971 | @ </div> |
| 972 | - @ <hr /> | |
| 972 | + @ <hr> | |
| 973 | 973 | }else{ |
| 974 | 974 | if( g.thTrace ){ |
| 975 | 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | - "}<br />\n", | |
| 976 | + "}<br>\n", | |
| 977 | 977 | blob_str(&tktchng)); |
| 978 | 978 | } |
| 979 | 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | 980 | rc = ticket_change(zUuid); |
| 981 | 981 | } |
| @@ -997,11 +997,11 @@ | ||
| 997 | 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | 998 | ** top of the screen. |
| 999 | 999 | */ |
| 1000 | 1000 | void tktnew_page(void){ |
| 1001 | 1001 | const char *zScript; |
| 1002 | - char *zEmail = 0, *zNewUuid = 0; | |
| 1002 | + char *zNewUuid = 0; | |
| 1003 | 1003 | int uid; |
| 1004 | 1004 | |
| 1005 | 1005 | login_check_credentials(); |
| 1006 | 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | ||
| 1008 | 1008 | cgi_redirect("home"); |
| 1009 | 1009 | } |
| 1010 | 1010 | style_set_current_feature("tkt"); |
| 1011 | 1011 | style_header("New Ticket"); |
| 1012 | 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | - if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); | |
| 1013 | + if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1); | |
| 1014 | 1014 | ticket_init(); |
| 1015 | 1015 | initializeVariablesFromCGI(); |
| 1016 | 1016 | getAllTicketFields(); |
| 1017 | 1017 | initializeVariablesFromDb(); |
| 1018 | 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | ||
| 1021 | 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | 1023 | } |
| 1024 | 1024 | zScript = ticket_newpage_code(); |
| 1025 | 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | - uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); | |
| 1026 | + int nEmail = 0; | |
| 1027 | + (void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail); | |
| 1028 | + uid = nEmail>0 | |
| 1029 | + ? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); | |
| 1027 | 1030 | if( uid ){ |
| 1028 | - zEmail = db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", | |
| 1029 | - uid); | |
| 1031 | + char * zEmail = | |
| 1032 | + db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", | |
| 1033 | + uid); | |
| 1030 | 1034 | if( zEmail ){ |
| 1031 | 1035 | Th_Store("private_contact", zEmail); |
| 1036 | + fossil_free(zEmail); | |
| 1032 | 1037 | } |
| 1033 | 1038 | } |
| 1034 | 1039 | } |
| 1035 | 1040 | Th_Store("login", login_name()); |
| 1036 | 1041 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1037 | 1042 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1038 | 1043 | (void*)&zNewUuid, 0); |
| 1039 | - if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); | |
| 1044 | + if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); | |
| 1040 | 1045 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1041 | 1046 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1042 | 1047 | return; |
| 1043 | 1048 | } |
| 1044 | 1049 | captcha_generate(0); |
| 1045 | 1050 | @ </form> |
| 1046 | - if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); | |
| 1051 | + if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); | |
| 1047 | 1052 | style_finish_page(); |
| 1048 | 1053 | } |
| 1049 | 1054 | |
| 1050 | 1055 | /* |
| 1051 | 1056 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | ||
| 1092 | 1097 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1093 | 1098 | @ "%h(zName)"</span> |
| 1094 | 1099 | style_finish_page(); |
| 1095 | 1100 | return; |
| 1096 | 1101 | } |
| 1097 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); | |
| 1102 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); | |
| 1098 | 1103 | ticket_init(); |
| 1099 | 1104 | getAllTicketFields(); |
| 1100 | 1105 | initializeVariablesFromCGI(); |
| 1101 | 1106 | initializeVariablesFromDb(); |
| 1102 | 1107 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1103 | 1108 | form_begin(0, "%R/%s", g.zPath); |
| 1104 | - @ <input type="hidden" name="name" value="%s(zName)" /> | |
| 1109 | + @ <input type="hidden" name="name" value="%s(zName)"> | |
| 1105 | 1110 | login_insert_csrf_secret(); |
| 1106 | 1111 | zScript = ticket_editpage_code(); |
| 1107 | 1112 | Th_Store("login", login_name()); |
| 1108 | 1113 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1109 | 1114 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1110 | 1115 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1111 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); | |
| 1116 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); | |
| 1112 | 1117 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1113 | 1118 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1114 | 1119 | return; |
| 1115 | 1120 | } |
| 1116 | 1121 | captcha_generate(0); |
| 1117 | 1122 | @ </form> |
| 1118 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); | |
| 1123 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); | |
| 1119 | 1124 | style_finish_page(); |
| 1120 | 1125 | } |
| 1121 | 1126 | |
| 1122 | 1127 | /* |
| 1123 | 1128 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1124 | 1129 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -764,27 +764,27 @@ | |
| 764 | } |
| 765 | } |
| 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | } |
| 769 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 770 | ticket_init(); |
| 771 | initializeVariablesFromCGI(); |
| 772 | getAllTicketFields(); |
| 773 | initializeVariablesFromDb(); |
| 774 | zScript = ticket_viewpage_code(); |
| 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); |
| 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | Th_Render(zScript); |
| 779 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); |
| 780 | |
| 781 | zFullName = db_text(0, |
| 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | if( zFullName ){ |
| 785 | attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>"); |
| 786 | } |
| 787 | |
| 788 | style_finish_page(); |
| 789 | } |
| 790 | |
| @@ -807,11 +807,11 @@ | |
| 807 | |
| 808 | if( argc!=3 ){ |
| 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | } |
| 811 | if( g.thTrace ){ |
| 812 | Th_Trace("append_field %#h {%#h}<br />\n", |
| 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | } |
| 815 | for(idx=0; idx<nField; idx++){ |
| 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | |
| 967 | @ <div style="color:blue"> |
| 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | @ </div> |
| 972 | @ <hr /> |
| 973 | }else{ |
| 974 | if( g.thTrace ){ |
| 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | "}<br />\n", |
| 977 | blob_str(&tktchng)); |
| 978 | } |
| 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | rc = ticket_change(zUuid); |
| 981 | } |
| @@ -997,11 +997,11 @@ | |
| 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | ** top of the screen. |
| 999 | */ |
| 1000 | void tktnew_page(void){ |
| 1001 | const char *zScript; |
| 1002 | char *zEmail = 0, *zNewUuid = 0; |
| 1003 | int uid; |
| 1004 | |
| 1005 | login_check_credentials(); |
| 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | |
| 1008 | cgi_redirect("home"); |
| 1009 | } |
| 1010 | style_set_current_feature("tkt"); |
| 1011 | style_header("New Ticket"); |
| 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); |
| 1014 | ticket_init(); |
| 1015 | initializeVariablesFromCGI(); |
| 1016 | getAllTicketFields(); |
| 1017 | initializeVariablesFromDb(); |
| 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | |
| 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | } |
| 1024 | zScript = ticket_newpage_code(); |
| 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); |
| 1027 | if( uid ){ |
| 1028 | zEmail = db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1029 | uid); |
| 1030 | if( zEmail ){ |
| 1031 | Th_Store("private_contact", zEmail); |
| 1032 | } |
| 1033 | } |
| 1034 | } |
| 1035 | Th_Store("login", login_name()); |
| 1036 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1037 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1038 | (void*)&zNewUuid, 0); |
| 1039 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); |
| 1040 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1041 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1042 | return; |
| 1043 | } |
| 1044 | captcha_generate(0); |
| 1045 | @ </form> |
| 1046 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); |
| 1047 | style_finish_page(); |
| 1048 | } |
| 1049 | |
| 1050 | /* |
| 1051 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | |
| 1092 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1093 | @ "%h(zName)"</span> |
| 1094 | style_finish_page(); |
| 1095 | return; |
| 1096 | } |
| 1097 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); |
| 1098 | ticket_init(); |
| 1099 | getAllTicketFields(); |
| 1100 | initializeVariablesFromCGI(); |
| 1101 | initializeVariablesFromDb(); |
| 1102 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1103 | form_begin(0, "%R/%s", g.zPath); |
| 1104 | @ <input type="hidden" name="name" value="%s(zName)" /> |
| 1105 | login_insert_csrf_secret(); |
| 1106 | zScript = ticket_editpage_code(); |
| 1107 | Th_Store("login", login_name()); |
| 1108 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1109 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1110 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1111 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); |
| 1112 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1113 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1114 | return; |
| 1115 | } |
| 1116 | captcha_generate(0); |
| 1117 | @ </form> |
| 1118 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); |
| 1119 | style_finish_page(); |
| 1120 | } |
| 1121 | |
| 1122 | /* |
| 1123 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1124 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -764,27 +764,27 @@ | |
| 764 | } |
| 765 | } |
| 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | } |
| 769 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 770 | ticket_init(); |
| 771 | initializeVariablesFromCGI(); |
| 772 | getAllTicketFields(); |
| 773 | initializeVariablesFromDb(); |
| 774 | zScript = ticket_viewpage_code(); |
| 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); |
| 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | Th_Render(zScript); |
| 779 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 780 | |
| 781 | zFullName = db_text(0, |
| 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | if( zFullName ){ |
| 785 | attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); |
| 786 | } |
| 787 | |
| 788 | style_finish_page(); |
| 789 | } |
| 790 | |
| @@ -807,11 +807,11 @@ | |
| 807 | |
| 808 | if( argc!=3 ){ |
| 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | } |
| 811 | if( g.thTrace ){ |
| 812 | Th_Trace("append_field %#h {%#h}<br>\n", |
| 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | } |
| 815 | for(idx=0; idx<nField; idx++){ |
| 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | |
| 967 | @ <div style="color:blue"> |
| 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | @ </div> |
| 972 | @ <hr> |
| 973 | }else{ |
| 974 | if( g.thTrace ){ |
| 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | "}<br>\n", |
| 977 | blob_str(&tktchng)); |
| 978 | } |
| 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | rc = ticket_change(zUuid); |
| 981 | } |
| @@ -997,11 +997,11 @@ | |
| 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | ** top of the screen. |
| 999 | */ |
| 1000 | void tktnew_page(void){ |
| 1001 | const char *zScript; |
| 1002 | char *zNewUuid = 0; |
| 1003 | int uid; |
| 1004 | |
| 1005 | login_check_credentials(); |
| 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | |
| 1008 | cgi_redirect("home"); |
| 1009 | } |
| 1010 | style_set_current_feature("tkt"); |
| 1011 | style_header("New Ticket"); |
| 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1); |
| 1014 | ticket_init(); |
| 1015 | initializeVariablesFromCGI(); |
| 1016 | getAllTicketFields(); |
| 1017 | initializeVariablesFromDb(); |
| 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | |
| 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | } |
| 1024 | zScript = ticket_newpage_code(); |
| 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | int nEmail = 0; |
| 1027 | (void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail); |
| 1028 | uid = nEmail>0 |
| 1029 | ? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); |
| 1030 | if( uid ){ |
| 1031 | char * zEmail = |
| 1032 | db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1033 | uid); |
| 1034 | if( zEmail ){ |
| 1035 | Th_Store("private_contact", zEmail); |
| 1036 | fossil_free(zEmail); |
| 1037 | } |
| 1038 | } |
| 1039 | } |
| 1040 | Th_Store("login", login_name()); |
| 1041 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1042 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1043 | (void*)&zNewUuid, 0); |
| 1044 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); |
| 1045 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1046 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1047 | return; |
| 1048 | } |
| 1049 | captcha_generate(0); |
| 1050 | @ </form> |
| 1051 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 1052 | style_finish_page(); |
| 1053 | } |
| 1054 | |
| 1055 | /* |
| 1056 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | |
| 1097 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1098 | @ "%h(zName)"</span> |
| 1099 | style_finish_page(); |
| 1100 | return; |
| 1101 | } |
| 1102 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); |
| 1103 | ticket_init(); |
| 1104 | getAllTicketFields(); |
| 1105 | initializeVariablesFromCGI(); |
| 1106 | initializeVariablesFromDb(); |
| 1107 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1108 | form_begin(0, "%R/%s", g.zPath); |
| 1109 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1110 | login_insert_csrf_secret(); |
| 1111 | zScript = ticket_editpage_code(); |
| 1112 | Th_Store("login", login_name()); |
| 1113 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1114 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1115 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1116 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); |
| 1117 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1118 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1119 | return; |
| 1120 | } |
| 1121 | captcha_generate(0); |
| 1122 | @ </form> |
| 1123 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); |
| 1124 | style_finish_page(); |
| 1125 | } |
| 1126 | |
| 1127 | /* |
| 1128 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1129 |
+23
-18
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -764,27 +764,27 @@ | ||
| 764 | 764 | } |
| 765 | 765 | } |
| 766 | 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | 768 | } |
| 769 | - if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); | |
| 769 | + if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); | |
| 770 | 770 | ticket_init(); |
| 771 | 771 | initializeVariablesFromCGI(); |
| 772 | 772 | getAllTicketFields(); |
| 773 | 773 | initializeVariablesFromDb(); |
| 774 | 774 | zScript = ticket_viewpage_code(); |
| 775 | 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | - if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); | |
| 776 | + if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); | |
| 777 | 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | 778 | Th_Render(zScript); |
| 779 | - if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); | |
| 779 | + if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); | |
| 780 | 780 | |
| 781 | 781 | zFullName = db_text(0, |
| 782 | 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | 784 | if( zFullName ){ |
| 785 | - attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>"); | |
| 785 | + attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); | |
| 786 | 786 | } |
| 787 | 787 | |
| 788 | 788 | style_finish_page(); |
| 789 | 789 | } |
| 790 | 790 | |
| @@ -807,11 +807,11 @@ | ||
| 807 | 807 | |
| 808 | 808 | if( argc!=3 ){ |
| 809 | 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | 810 | } |
| 811 | 811 | if( g.thTrace ){ |
| 812 | - Th_Trace("append_field %#h {%#h}<br />\n", | |
| 812 | + Th_Trace("append_field %#h {%#h}<br>\n", | |
| 813 | 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | 814 | } |
| 815 | 815 | for(idx=0; idx<nField; idx++){ |
| 816 | 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | ||
| 967 | 967 | @ <div style="color:blue"> |
| 968 | 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | 971 | @ </div> |
| 972 | - @ <hr /> | |
| 972 | + @ <hr> | |
| 973 | 973 | }else{ |
| 974 | 974 | if( g.thTrace ){ |
| 975 | 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | - "}<br />\n", | |
| 976 | + "}<br>\n", | |
| 977 | 977 | blob_str(&tktchng)); |
| 978 | 978 | } |
| 979 | 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | 980 | rc = ticket_change(zUuid); |
| 981 | 981 | } |
| @@ -997,11 +997,11 @@ | ||
| 997 | 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | 998 | ** top of the screen. |
| 999 | 999 | */ |
| 1000 | 1000 | void tktnew_page(void){ |
| 1001 | 1001 | const char *zScript; |
| 1002 | - char *zEmail = 0, *zNewUuid = 0; | |
| 1002 | + char *zNewUuid = 0; | |
| 1003 | 1003 | int uid; |
| 1004 | 1004 | |
| 1005 | 1005 | login_check_credentials(); |
| 1006 | 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | ||
| 1008 | 1008 | cgi_redirect("home"); |
| 1009 | 1009 | } |
| 1010 | 1010 | style_set_current_feature("tkt"); |
| 1011 | 1011 | style_header("New Ticket"); |
| 1012 | 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | - if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); | |
| 1013 | + if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1); | |
| 1014 | 1014 | ticket_init(); |
| 1015 | 1015 | initializeVariablesFromCGI(); |
| 1016 | 1016 | getAllTicketFields(); |
| 1017 | 1017 | initializeVariablesFromDb(); |
| 1018 | 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | ||
| 1021 | 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | 1023 | } |
| 1024 | 1024 | zScript = ticket_newpage_code(); |
| 1025 | 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | - uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); | |
| 1026 | + int nEmail = 0; | |
| 1027 | + (void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail); | |
| 1028 | + uid = nEmail>0 | |
| 1029 | + ? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); | |
| 1027 | 1030 | if( uid ){ |
| 1028 | - zEmail = db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", | |
| 1029 | - uid); | |
| 1031 | + char * zEmail = | |
| 1032 | + db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", | |
| 1033 | + uid); | |
| 1030 | 1034 | if( zEmail ){ |
| 1031 | 1035 | Th_Store("private_contact", zEmail); |
| 1036 | + fossil_free(zEmail); | |
| 1032 | 1037 | } |
| 1033 | 1038 | } |
| 1034 | 1039 | } |
| 1035 | 1040 | Th_Store("login", login_name()); |
| 1036 | 1041 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1037 | 1042 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1038 | 1043 | (void*)&zNewUuid, 0); |
| 1039 | - if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); | |
| 1044 | + if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); | |
| 1040 | 1045 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1041 | 1046 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1042 | 1047 | return; |
| 1043 | 1048 | } |
| 1044 | 1049 | captcha_generate(0); |
| 1045 | 1050 | @ </form> |
| 1046 | - if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); | |
| 1051 | + if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); | |
| 1047 | 1052 | style_finish_page(); |
| 1048 | 1053 | } |
| 1049 | 1054 | |
| 1050 | 1055 | /* |
| 1051 | 1056 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | ||
| 1092 | 1097 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1093 | 1098 | @ "%h(zName)"</span> |
| 1094 | 1099 | style_finish_page(); |
| 1095 | 1100 | return; |
| 1096 | 1101 | } |
| 1097 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); | |
| 1102 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); | |
| 1098 | 1103 | ticket_init(); |
| 1099 | 1104 | getAllTicketFields(); |
| 1100 | 1105 | initializeVariablesFromCGI(); |
| 1101 | 1106 | initializeVariablesFromDb(); |
| 1102 | 1107 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1103 | 1108 | form_begin(0, "%R/%s", g.zPath); |
| 1104 | - @ <input type="hidden" name="name" value="%s(zName)" /> | |
| 1109 | + @ <input type="hidden" name="name" value="%s(zName)"> | |
| 1105 | 1110 | login_insert_csrf_secret(); |
| 1106 | 1111 | zScript = ticket_editpage_code(); |
| 1107 | 1112 | Th_Store("login", login_name()); |
| 1108 | 1113 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1109 | 1114 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1110 | 1115 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1111 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); | |
| 1116 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); | |
| 1112 | 1117 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1113 | 1118 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1114 | 1119 | return; |
| 1115 | 1120 | } |
| 1116 | 1121 | captcha_generate(0); |
| 1117 | 1122 | @ </form> |
| 1118 | - if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); | |
| 1123 | + if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); | |
| 1119 | 1124 | style_finish_page(); |
| 1120 | 1125 | } |
| 1121 | 1126 | |
| 1122 | 1127 | /* |
| 1123 | 1128 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1124 | 1129 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -764,27 +764,27 @@ | |
| 764 | } |
| 765 | } |
| 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | } |
| 769 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); |
| 770 | ticket_init(); |
| 771 | initializeVariablesFromCGI(); |
| 772 | getAllTicketFields(); |
| 773 | initializeVariablesFromDb(); |
| 774 | zScript = ticket_viewpage_code(); |
| 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); |
| 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | Th_Render(zScript); |
| 779 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); |
| 780 | |
| 781 | zFullName = db_text(0, |
| 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | if( zFullName ){ |
| 785 | attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>"); |
| 786 | } |
| 787 | |
| 788 | style_finish_page(); |
| 789 | } |
| 790 | |
| @@ -807,11 +807,11 @@ | |
| 807 | |
| 808 | if( argc!=3 ){ |
| 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | } |
| 811 | if( g.thTrace ){ |
| 812 | Th_Trace("append_field %#h {%#h}<br />\n", |
| 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | } |
| 815 | for(idx=0; idx<nField; idx++){ |
| 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | |
| 967 | @ <div style="color:blue"> |
| 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | @ </div> |
| 972 | @ <hr /> |
| 973 | }else{ |
| 974 | if( g.thTrace ){ |
| 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | "}<br />\n", |
| 977 | blob_str(&tktchng)); |
| 978 | } |
| 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | rc = ticket_change(zUuid); |
| 981 | } |
| @@ -997,11 +997,11 @@ | |
| 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | ** top of the screen. |
| 999 | */ |
| 1000 | void tktnew_page(void){ |
| 1001 | const char *zScript; |
| 1002 | char *zEmail = 0, *zNewUuid = 0; |
| 1003 | int uid; |
| 1004 | |
| 1005 | login_check_credentials(); |
| 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | |
| 1008 | cgi_redirect("home"); |
| 1009 | } |
| 1010 | style_set_current_feature("tkt"); |
| 1011 | style_header("New Ticket"); |
| 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); |
| 1014 | ticket_init(); |
| 1015 | initializeVariablesFromCGI(); |
| 1016 | getAllTicketFields(); |
| 1017 | initializeVariablesFromDb(); |
| 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | |
| 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | } |
| 1024 | zScript = ticket_newpage_code(); |
| 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); |
| 1027 | if( uid ){ |
| 1028 | zEmail = db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1029 | uid); |
| 1030 | if( zEmail ){ |
| 1031 | Th_Store("private_contact", zEmail); |
| 1032 | } |
| 1033 | } |
| 1034 | } |
| 1035 | Th_Store("login", login_name()); |
| 1036 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1037 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1038 | (void*)&zNewUuid, 0); |
| 1039 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); |
| 1040 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1041 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1042 | return; |
| 1043 | } |
| 1044 | captcha_generate(0); |
| 1045 | @ </form> |
| 1046 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); |
| 1047 | style_finish_page(); |
| 1048 | } |
| 1049 | |
| 1050 | /* |
| 1051 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | |
| 1092 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1093 | @ "%h(zName)"</span> |
| 1094 | style_finish_page(); |
| 1095 | return; |
| 1096 | } |
| 1097 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); |
| 1098 | ticket_init(); |
| 1099 | getAllTicketFields(); |
| 1100 | initializeVariablesFromCGI(); |
| 1101 | initializeVariablesFromDb(); |
| 1102 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1103 | form_begin(0, "%R/%s", g.zPath); |
| 1104 | @ <input type="hidden" name="name" value="%s(zName)" /> |
| 1105 | login_insert_csrf_secret(); |
| 1106 | zScript = ticket_editpage_code(); |
| 1107 | Th_Store("login", login_name()); |
| 1108 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1109 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1110 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1111 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); |
| 1112 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1113 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1114 | return; |
| 1115 | } |
| 1116 | captcha_generate(0); |
| 1117 | @ </form> |
| 1118 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); |
| 1119 | style_finish_page(); |
| 1120 | } |
| 1121 | |
| 1122 | /* |
| 1123 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1124 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -764,27 +764,27 @@ | |
| 764 | } |
| 765 | } |
| 766 | if( !showTimeline && g.perm.Hyperlink ){ |
| 767 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 768 | } |
| 769 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 770 | ticket_init(); |
| 771 | initializeVariablesFromCGI(); |
| 772 | getAllTicketFields(); |
| 773 | initializeVariablesFromDb(); |
| 774 | zScript = ticket_viewpage_code(); |
| 775 | if( P("showfields")!=0 ) showAllFields(); |
| 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); |
| 777 | safe_html_context(DOCSRC_TICKET); |
| 778 | Th_Render(zScript); |
| 779 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 780 | |
| 781 | zFullName = db_text(0, |
| 782 | "SELECT tkt_uuid FROM ticket" |
| 783 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 784 | if( zFullName ){ |
| 785 | attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>"); |
| 786 | } |
| 787 | |
| 788 | style_finish_page(); |
| 789 | } |
| 790 | |
| @@ -807,11 +807,11 @@ | |
| 807 | |
| 808 | if( argc!=3 ){ |
| 809 | return Th_WrongNumArgs(interp, "append_field FIELD STRING"); |
| 810 | } |
| 811 | if( g.thTrace ){ |
| 812 | Th_Trace("append_field %#h {%#h}<br>\n", |
| 813 | argl[1], argv[1], argl[2], argv[2]); |
| 814 | } |
| 815 | for(idx=0; idx<nField; idx++){ |
| 816 | if( memcmp(aField[idx].zName, argv[1], argl[1])==0 |
| 817 | && aField[idx].zName[argl[1]]==0 ){ |
| @@ -967,15 +967,15 @@ | |
| 967 | @ <div style="color:blue"> |
| 968 | @ <p>Ticket artifact that would have been submitted:</p> |
| 969 | @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> |
| 970 | @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> |
| 971 | @ </div> |
| 972 | @ <hr> |
| 973 | }else{ |
| 974 | if( g.thTrace ){ |
| 975 | Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" |
| 976 | "}<br>\n", |
| 977 | blob_str(&tktchng)); |
| 978 | } |
| 979 | ticket_put(&tktchng, zUuid, aUsed, needMod); |
| 980 | rc = ticket_change(zUuid); |
| 981 | } |
| @@ -997,11 +997,11 @@ | |
| 997 | ** when you press submit - it just prints the ticket artifact at the |
| 998 | ** top of the screen. |
| 999 | */ |
| 1000 | void tktnew_page(void){ |
| 1001 | const char *zScript; |
| 1002 | char *zNewUuid = 0; |
| 1003 | int uid; |
| 1004 | |
| 1005 | login_check_credentials(); |
| 1006 | if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } |
| 1007 | if( P("cancel") ){ |
| @@ -1008,11 +1008,11 @@ | |
| 1008 | cgi_redirect("home"); |
| 1009 | } |
| 1010 | style_set_current_feature("tkt"); |
| 1011 | style_header("New Ticket"); |
| 1012 | ticket_standard_submenu(T_ALL_BUT(T_NEW)); |
| 1013 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1); |
| 1014 | ticket_init(); |
| 1015 | initializeVariablesFromCGI(); |
| 1016 | getAllTicketFields(); |
| 1017 | initializeVariablesFromDb(); |
| 1018 | if( g.zPath[0]=='d' ) showAllFields(); |
| @@ -1021,31 +1021,36 @@ | |
| 1021 | if( P("date_override") && g.perm.Setup ){ |
| 1022 | @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> |
| 1023 | } |
| 1024 | zScript = ticket_newpage_code(); |
| 1025 | if( g.zLogin && g.zLogin[0] ){ |
| 1026 | int nEmail = 0; |
| 1027 | (void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail); |
| 1028 | uid = nEmail>0 |
| 1029 | ? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin); |
| 1030 | if( uid ){ |
| 1031 | char * zEmail = |
| 1032 | db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d", |
| 1033 | uid); |
| 1034 | if( zEmail ){ |
| 1035 | Th_Store("private_contact", zEmail); |
| 1036 | fossil_free(zEmail); |
| 1037 | } |
| 1038 | } |
| 1039 | } |
| 1040 | Th_Store("login", login_name()); |
| 1041 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1042 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, |
| 1043 | (void*)&zNewUuid, 0); |
| 1044 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1); |
| 1045 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ |
| 1046 | cgi_redirect(mprintf("%R/tktview/%s", zNewUuid)); |
| 1047 | return; |
| 1048 | } |
| 1049 | captcha_generate(0); |
| 1050 | @ </form> |
| 1051 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 1052 | style_finish_page(); |
| 1053 | } |
| 1054 | |
| 1055 | /* |
| 1056 | ** WEBPAGE: tktedit |
| @@ -1092,32 +1097,32 @@ | |
| 1097 | @ <span class="tktError">%d(nRec) tickets begin with: |
| 1098 | @ "%h(zName)"</span> |
| 1099 | style_finish_page(); |
| 1100 | return; |
| 1101 | } |
| 1102 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); |
| 1103 | ticket_init(); |
| 1104 | getAllTicketFields(); |
| 1105 | initializeVariablesFromCGI(); |
| 1106 | initializeVariablesFromDb(); |
| 1107 | if( g.zPath[0]=='d' ) showAllFields(); |
| 1108 | form_begin(0, "%R/%s", g.zPath); |
| 1109 | @ <input type="hidden" name="name" value="%s(zName)"> |
| 1110 | login_insert_csrf_secret(); |
| 1111 | zScript = ticket_editpage_code(); |
| 1112 | Th_Store("login", login_name()); |
| 1113 | Th_Store("date", db_text(0, "SELECT datetime('now')")); |
| 1114 | Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); |
| 1115 | Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); |
| 1116 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1); |
| 1117 | if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ |
| 1118 | cgi_redirect(mprintf("%R/tktview/%s", zName)); |
| 1119 | return; |
| 1120 | } |
| 1121 | captcha_generate(0); |
| 1122 | @ </form> |
| 1123 | if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1); |
| 1124 | style_finish_page(); |
| 1125 | } |
| 1126 | |
| 1127 | /* |
| 1128 | ** Check the ticket table schema in zSchema to see if it appears to |
| 1129 |
+31
-32
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -156,16 +156,16 @@ | ||
| 156 | 156 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 157 | 157 | login_insert_csrf_secret(); |
| 158 | 158 | @ <p>%s(zDesc)</p> |
| 159 | 159 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 160 | 160 | @ <blockquote><p> |
| 161 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 162 | - @ <input type="submit" name="clear" value="Revert To Default" /> | |
| 163 | - @ <input type="submit" name="setup" value="Cancel" /> | |
| 161 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 162 | + @ <input type="submit" name="clear" value="Revert To Default"> | |
| 163 | + @ <input type="submit" name="setup" value="Cancel"> | |
| 164 | 164 | @ </p></blockquote> |
| 165 | 165 | @ </div></form> |
| 166 | - @ <hr /> | |
| 166 | + @ <hr> | |
| 167 | 167 | @ <h2>Default %s(zTitle)</h2> |
| 168 | 168 | @ <blockquote><pre> |
| 169 | 169 | @ %h(zDfltValue) |
| 170 | 170 | @ </pre></blockquote> |
| 171 | 171 | style_finish_page(); |
| @@ -324,12 +324,12 @@ | ||
| 324 | 324 | @ </th1> |
| 325 | 325 | @ <h1 style="text-align: center;">Enter A New Ticket</h1> |
| 326 | 326 | @ <table cellpadding="5"> |
| 327 | 327 | @ <tr> |
| 328 | 328 | @ <td colspan="3"> |
| 329 | -@ Enter a one-line summary of the ticket:<br /> | |
| 330 | -@ <input type="text" name="title" size="60" value="$<title>" /> | |
| 329 | +@ Enter a one-line summary of the ticket:<br> | |
| 330 | +@ <input type="text" name="title" size="60" value="$<title>"> | |
| 331 | 331 | @ </td> |
| 332 | 332 | @ </tr> |
| 333 | 333 | @ |
| 334 | 334 | @ <tr> |
| 335 | 335 | @ <td align="right">Type:</td> |
| @@ -338,11 +338,11 @@ | ||
| 338 | 338 | @ </tr> |
| 339 | 339 | @ |
| 340 | 340 | @ <tr> |
| 341 | 341 | @ <td align="right">Version:</td> |
| 342 | 342 | @ <td align="left"> |
| 343 | -@ <input type="text" name="foundin" size="20" value="$<foundin>" /> | |
| 343 | +@ <input type="text" name="foundin" size="20" value="$<foundin>"> | |
| 344 | 344 | @ </td> |
| 345 | 345 | @ <td align="left">In what version or build number do you observe |
| 346 | 346 | @ the problem?</td> |
| 347 | 347 | @ </tr> |
| 348 | 348 | @ |
| @@ -354,12 +354,11 @@ | ||
| 354 | 354 | @ </tr> |
| 355 | 355 | @ |
| 356 | 356 | @ <tr> |
| 357 | 357 | @ <td align="right">EMail:</td> |
| 358 | 358 | @ <td align="left"> |
| 359 | -@ <input type="text" name="private_contact" value="$<private_contact>" | |
| 360 | -@ size="30" /> | |
| 359 | +@ <input name="private_contact" value="$<private_contact>" size="30"> | |
| 361 | 360 | @ </td> |
| 362 | 361 | @ <td align="left"><u>Not publicly visible</u> |
| 363 | 362 | @ Used by developers to contact you with questions.</td> |
| 364 | 363 | @ </tr> |
| 365 | 364 | @ |
| @@ -368,19 +367,19 @@ | ||
| 368 | 367 | @ Enter a detailed description of the problem. |
| 369 | 368 | @ For code defects, be sure to provide details on exactly how |
| 370 | 369 | @ the problem can be reproduced. Provide as much detail as |
| 371 | 370 | @ possible. Format: |
| 372 | 371 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 373 | -@ <br /> | |
| 372 | +@ <br> | |
| 374 | 373 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 375 | 374 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 376 | -@ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> | |
| 375 | +@ wrap="virtual" class="wikiedit">$<icomment></textarea><br> | |
| 377 | 376 | @ </tr> |
| 378 | 377 | @ |
| 379 | 378 | @ <th1>enable_output [info exists preview]</th1> |
| 380 | 379 | @ <tr><td colspan="3"> |
| 381 | -@ Description Preview:<br /><hr /> | |
| 380 | +@ Description Preview:<br><hr> | |
| 382 | 381 | @ <th1> |
| 383 | 382 | @ if {$mutype eq "Wiki"} { |
| 384 | 383 | @ wiki $icomment |
| 385 | 384 | @ } elseif {$mutype eq "Plain Text"} { |
| 386 | 385 | @ set r [randhex] |
| @@ -392,33 +391,33 @@ | ||
| 392 | 391 | @ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>" |
| 393 | 392 | @ } else { |
| 394 | 393 | @ wiki "<nowiki>$icomment\n</nowiki>" |
| 395 | 394 | @ } |
| 396 | 395 | @ </th1> |
| 397 | -@ <hr /></td></tr> | |
| 396 | +@ <hr></td></tr> | |
| 398 | 397 | @ <th1>enable_output 1</th1> |
| 399 | 398 | @ |
| 400 | 399 | @ <tr> |
| 401 | 400 | @ <td><td align="left"> |
| 402 | -@ <input type="submit" name="preview" value="Preview" /> | |
| 401 | +@ <input type="submit" name="preview" value="Preview"> | |
| 403 | 402 | @ </td> |
| 404 | 403 | @ <td align="left">See how the description will appear after formatting.</td> |
| 405 | 404 | @ </tr> |
| 406 | 405 | @ |
| 407 | 406 | @ <th1>enable_output [info exists preview]</th1> |
| 408 | 407 | @ <tr> |
| 409 | 408 | @ <td><td align="left"> |
| 410 | -@ <input type="submit" name="submit" value="Submit" /> | |
| 409 | +@ <input type="submit" name="submit" value="Submit"> | |
| 411 | 410 | @ </td> |
| 412 | 411 | @ <td align="left">After filling in the information above, press this |
| 413 | 412 | @ button to create the new ticket</td> |
| 414 | 413 | @ </tr> |
| 415 | 414 | @ <th1>enable_output 1</th1> |
| 416 | 415 | @ |
| 417 | 416 | @ <tr> |
| 418 | 417 | @ <td><td align="left"> |
| 419 | -@ <input type="submit" name="cancel" value="Cancel" /> | |
| 418 | +@ <input type="submit" name="cancel" value="Cancel"> | |
| 420 | 419 | @ </td> |
| 421 | 420 | @ <td>Abandon and forget this ticket</td> |
| 422 | 421 | @ </tr> |
| 423 | 422 | @ </table> |
| 424 | 423 | ; |
| @@ -532,11 +531,11 @@ | ||
| 532 | 531 | @ mimetype as xmimetype, icomment AS xcomment, |
| 533 | 532 | @ username AS xusername |
| 534 | 533 | @ FROM ticketchng |
| 535 | 534 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 536 | 535 | @ if {$seenRow} { |
| 537 | -@ html "<hr />\n" | |
| 536 | +@ html "<hr>\n" | |
| 538 | 537 | @ } else { |
| 539 | 538 | @ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" |
| 540 | 539 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 541 | 540 | @ set seenRow 1 |
| 542 | 541 | @ } |
| @@ -616,11 +615,11 @@ | ||
| 616 | 615 | @ set preview 1 |
| 617 | 616 | @ } |
| 618 | 617 | @ </th1> |
| 619 | 618 | @ <table cellpadding="5"> |
| 620 | 619 | @ <tr><td class="tktDspLabel">Title:</td><td> |
| 621 | -@ <input type="text" name="title" value="$<title>" size="60" /> | |
| 620 | +@ <input type="text" name="title" value="$<title>" size="60"> | |
| 622 | 621 | @ </td></tr> |
| 623 | 622 | @ |
| 624 | 623 | @ <tr><td class="tktDspLabel">Status:</td><td> |
| 625 | 624 | @ <th1>combobox status $status_choices 1</th1> |
| 626 | 625 | @ </td></tr> |
| @@ -646,30 +645,30 @@ | ||
| 646 | 645 | @ </td></tr> |
| 647 | 646 | @ |
| 648 | 647 | @ <th1>enable_output [hascap e]</th1> |
| 649 | 648 | @ <tr><td class="tktDspLabel">Contact:</td><td> |
| 650 | 649 | @ <input type="text" name="private_contact" size="40" |
| 651 | -@ value="$<private_contact>" /> | |
| 650 | +@ value="$<private_contact>"> | |
| 652 | 651 | @ </td></tr> |
| 653 | 652 | @ <th1>enable_output 1</th1> |
| 654 | 653 | @ |
| 655 | 654 | @ <tr><td class="tktDspLabel">Version Found In:</td><td> |
| 656 | -@ <input type="text" name="foundin" size="50" value="$<foundin>" /> | |
| 655 | +@ <input type="text" name="foundin" size="50" value="$<foundin>"> | |
| 657 | 656 | @ </td></tr> |
| 658 | 657 | @ |
| 659 | 658 | @ <tr><td colspan="2"> |
| 660 | 659 | @ Append Remark with format |
| 661 | 660 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 662 | 661 | @ from |
| 663 | -@ <input type="text" name="username" value="$<username>" size="30" />:<br /> | |
| 662 | +@ <input type="text" name="username" value="$<username>" size="30">:<br> | |
| 664 | 663 | @ <textarea name="icomment" cols="80" rows="15" |
| 665 | 664 | @ wrap="virtual" class="wikiedit">$<icomment></textarea> |
| 666 | 665 | @ </td></tr> |
| 667 | 666 | @ |
| 668 | 667 | @ <th1>enable_output [info exists preview]</th1> |
| 669 | 668 | @ <tr><td colspan="2"> |
| 670 | -@ Description Preview:<br /><hr /> | |
| 669 | +@ Description Preview:<br><hr> | |
| 671 | 670 | @ <th1> |
| 672 | 671 | @ if {$mutype eq "Wiki"} { |
| 673 | 672 | @ wiki $icomment |
| 674 | 673 | @ } elseif {$mutype eq "Plain Text"} { |
| 675 | 674 | @ set r [randhex] |
| @@ -681,33 +680,33 @@ | ||
| 681 | 680 | @ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>" |
| 682 | 681 | @ } else { |
| 683 | 682 | @ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>" |
| 684 | 683 | @ } |
| 685 | 684 | @ </th1> |
| 686 | -@ <hr /> | |
| 685 | +@ <hr> | |
| 687 | 686 | @ </td></tr> |
| 688 | 687 | @ <th1>enable_output 1</th1> |
| 689 | 688 | @ |
| 690 | 689 | @ <tr> |
| 691 | 690 | @ <td align="right"> |
| 692 | -@ <input type="submit" name="preview" value="Preview" /> | |
| 691 | +@ <input type="submit" name="preview" value="Preview"> | |
| 693 | 692 | @ </td> |
| 694 | 693 | @ <td align="left">See how the description will appear after formatting.</td> |
| 695 | 694 | @ </tr> |
| 696 | 695 | @ |
| 697 | 696 | @ <th1>enable_output [info exists preview]</th1> |
| 698 | 697 | @ <tr> |
| 699 | 698 | @ <td align="right"> |
| 700 | -@ <input type="submit" name="submit" value="Submit" /> | |
| 699 | +@ <input type="submit" name="submit" value="Submit"> | |
| 701 | 700 | @ </td> |
| 702 | 701 | @ <td align="left">Apply the changes shown above</td> |
| 703 | 702 | @ </tr> |
| 704 | 703 | @ <th1>enable_output 1</th1> |
| 705 | 704 | @ |
| 706 | 705 | @ <tr> |
| 707 | 706 | @ <td align="right"> |
| 708 | -@ <input type="submit" name="cancel" value="Cancel" /> | |
| 707 | +@ <input type="submit" name="cancel" value="Cancel"> | |
| 709 | 708 | @ </td> |
| 710 | 709 | @ <td>Abandon this edit</td> |
| 711 | 710 | @ </tr> |
| 712 | 711 | @ |
| 713 | 712 | @ </table> |
| @@ -912,36 +911,36 @@ | ||
| 912 | 911 | style_header("Ticket Display On Timelines"); |
| 913 | 912 | db_begin_transaction(); |
| 914 | 913 | @ <form action="%R/tktsetup_timeline" method="post"><div> |
| 915 | 914 | login_insert_csrf_secret(); |
| 916 | 915 | |
| 917 | - @ <hr /> | |
| 916 | + @ <hr> | |
| 918 | 917 | entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", |
| 919 | 918 | "title", 0); |
| 920 | 919 | @ <p>An SQL expression in a query against the TICKET table that will |
| 921 | 920 | @ return the title of the ticket for display purposes. |
| 922 | 921 | @ (Property: ticket-title-expr)</p> |
| 923 | 922 | |
| 924 | - @ <hr /> | |
| 923 | + @ <hr> | |
| 925 | 924 | entry_attribute("Ticket Status", 40, "ticket-status-column", "s", |
| 926 | 925 | "status", 0); |
| 927 | 926 | @ <p>The name of the column in the TICKET table that contains the ticket |
| 928 | 927 | @ status in human-readable form. Case sensitive. |
| 929 | 928 | @ (Property: ticket-status-column)</p> |
| 930 | 929 | |
| 931 | - @ <hr /> | |
| 930 | + @ <hr> | |
| 932 | 931 | entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c", |
| 933 | 932 | "status='Closed'", 0); |
| 934 | 933 | @ <p>An SQL expression that evaluates to true in a TICKET table query if |
| 935 | 934 | @ the ticket is closed. |
| 936 | 935 | @ (Property: ticket-closed-expr)</p> |
| 937 | 936 | |
| 938 | - @ <hr /> | |
| 937 | + @ <hr> | |
| 939 | 938 | @ <p> |
| 940 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 941 | - @ <input type="submit" name="setup" value="Cancel" /> | |
| 939 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 940 | + @ <input type="submit" name="setup" value="Cancel"> | |
| 942 | 941 | @ </p> |
| 943 | 942 | @ </div></form> |
| 944 | 943 | db_end_transaction(0); |
| 945 | 944 | style_finish_page(); |
| 946 | 945 | |
| 947 | 946 | } |
| 948 | 947 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -156,16 +156,16 @@ | |
| 156 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 157 | login_insert_csrf_secret(); |
| 158 | @ <p>%s(zDesc)</p> |
| 159 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 160 | @ <blockquote><p> |
| 161 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 162 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 163 | @ <input type="submit" name="setup" value="Cancel" /> |
| 164 | @ </p></blockquote> |
| 165 | @ </div></form> |
| 166 | @ <hr /> |
| 167 | @ <h2>Default %s(zTitle)</h2> |
| 168 | @ <blockquote><pre> |
| 169 | @ %h(zDfltValue) |
| 170 | @ </pre></blockquote> |
| 171 | style_finish_page(); |
| @@ -324,12 +324,12 @@ | |
| 324 | @ </th1> |
| 325 | @ <h1 style="text-align: center;">Enter A New Ticket</h1> |
| 326 | @ <table cellpadding="5"> |
| 327 | @ <tr> |
| 328 | @ <td colspan="3"> |
| 329 | @ Enter a one-line summary of the ticket:<br /> |
| 330 | @ <input type="text" name="title" size="60" value="$<title>" /> |
| 331 | @ </td> |
| 332 | @ </tr> |
| 333 | @ |
| 334 | @ <tr> |
| 335 | @ <td align="right">Type:</td> |
| @@ -338,11 +338,11 @@ | |
| 338 | @ </tr> |
| 339 | @ |
| 340 | @ <tr> |
| 341 | @ <td align="right">Version:</td> |
| 342 | @ <td align="left"> |
| 343 | @ <input type="text" name="foundin" size="20" value="$<foundin>" /> |
| 344 | @ </td> |
| 345 | @ <td align="left">In what version or build number do you observe |
| 346 | @ the problem?</td> |
| 347 | @ </tr> |
| 348 | @ |
| @@ -354,12 +354,11 @@ | |
| 354 | @ </tr> |
| 355 | @ |
| 356 | @ <tr> |
| 357 | @ <td align="right">EMail:</td> |
| 358 | @ <td align="left"> |
| 359 | @ <input type="text" name="private_contact" value="$<private_contact>" |
| 360 | @ size="30" /> |
| 361 | @ </td> |
| 362 | @ <td align="left"><u>Not publicly visible</u> |
| 363 | @ Used by developers to contact you with questions.</td> |
| 364 | @ </tr> |
| 365 | @ |
| @@ -368,19 +367,19 @@ | |
| 368 | @ Enter a detailed description of the problem. |
| 369 | @ For code defects, be sure to provide details on exactly how |
| 370 | @ the problem can be reproduced. Provide as much detail as |
| 371 | @ possible. Format: |
| 372 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 373 | @ <br /> |
| 374 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 375 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 376 | @ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> |
| 377 | @ </tr> |
| 378 | @ |
| 379 | @ <th1>enable_output [info exists preview]</th1> |
| 380 | @ <tr><td colspan="3"> |
| 381 | @ Description Preview:<br /><hr /> |
| 382 | @ <th1> |
| 383 | @ if {$mutype eq "Wiki"} { |
| 384 | @ wiki $icomment |
| 385 | @ } elseif {$mutype eq "Plain Text"} { |
| 386 | @ set r [randhex] |
| @@ -392,33 +391,33 @@ | |
| 392 | @ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>" |
| 393 | @ } else { |
| 394 | @ wiki "<nowiki>$icomment\n</nowiki>" |
| 395 | @ } |
| 396 | @ </th1> |
| 397 | @ <hr /></td></tr> |
| 398 | @ <th1>enable_output 1</th1> |
| 399 | @ |
| 400 | @ <tr> |
| 401 | @ <td><td align="left"> |
| 402 | @ <input type="submit" name="preview" value="Preview" /> |
| 403 | @ </td> |
| 404 | @ <td align="left">See how the description will appear after formatting.</td> |
| 405 | @ </tr> |
| 406 | @ |
| 407 | @ <th1>enable_output [info exists preview]</th1> |
| 408 | @ <tr> |
| 409 | @ <td><td align="left"> |
| 410 | @ <input type="submit" name="submit" value="Submit" /> |
| 411 | @ </td> |
| 412 | @ <td align="left">After filling in the information above, press this |
| 413 | @ button to create the new ticket</td> |
| 414 | @ </tr> |
| 415 | @ <th1>enable_output 1</th1> |
| 416 | @ |
| 417 | @ <tr> |
| 418 | @ <td><td align="left"> |
| 419 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 420 | @ </td> |
| 421 | @ <td>Abandon and forget this ticket</td> |
| 422 | @ </tr> |
| 423 | @ </table> |
| 424 | ; |
| @@ -532,11 +531,11 @@ | |
| 532 | @ mimetype as xmimetype, icomment AS xcomment, |
| 533 | @ username AS xusername |
| 534 | @ FROM ticketchng |
| 535 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 536 | @ if {$seenRow} { |
| 537 | @ html "<hr />\n" |
| 538 | @ } else { |
| 539 | @ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" |
| 540 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 541 | @ set seenRow 1 |
| 542 | @ } |
| @@ -616,11 +615,11 @@ | |
| 616 | @ set preview 1 |
| 617 | @ } |
| 618 | @ </th1> |
| 619 | @ <table cellpadding="5"> |
| 620 | @ <tr><td class="tktDspLabel">Title:</td><td> |
| 621 | @ <input type="text" name="title" value="$<title>" size="60" /> |
| 622 | @ </td></tr> |
| 623 | @ |
| 624 | @ <tr><td class="tktDspLabel">Status:</td><td> |
| 625 | @ <th1>combobox status $status_choices 1</th1> |
| 626 | @ </td></tr> |
| @@ -646,30 +645,30 @@ | |
| 646 | @ </td></tr> |
| 647 | @ |
| 648 | @ <th1>enable_output [hascap e]</th1> |
| 649 | @ <tr><td class="tktDspLabel">Contact:</td><td> |
| 650 | @ <input type="text" name="private_contact" size="40" |
| 651 | @ value="$<private_contact>" /> |
| 652 | @ </td></tr> |
| 653 | @ <th1>enable_output 1</th1> |
| 654 | @ |
| 655 | @ <tr><td class="tktDspLabel">Version Found In:</td><td> |
| 656 | @ <input type="text" name="foundin" size="50" value="$<foundin>" /> |
| 657 | @ </td></tr> |
| 658 | @ |
| 659 | @ <tr><td colspan="2"> |
| 660 | @ Append Remark with format |
| 661 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 662 | @ from |
| 663 | @ <input type="text" name="username" value="$<username>" size="30" />:<br /> |
| 664 | @ <textarea name="icomment" cols="80" rows="15" |
| 665 | @ wrap="virtual" class="wikiedit">$<icomment></textarea> |
| 666 | @ </td></tr> |
| 667 | @ |
| 668 | @ <th1>enable_output [info exists preview]</th1> |
| 669 | @ <tr><td colspan="2"> |
| 670 | @ Description Preview:<br /><hr /> |
| 671 | @ <th1> |
| 672 | @ if {$mutype eq "Wiki"} { |
| 673 | @ wiki $icomment |
| 674 | @ } elseif {$mutype eq "Plain Text"} { |
| 675 | @ set r [randhex] |
| @@ -681,33 +680,33 @@ | |
| 681 | @ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>" |
| 682 | @ } else { |
| 683 | @ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>" |
| 684 | @ } |
| 685 | @ </th1> |
| 686 | @ <hr /> |
| 687 | @ </td></tr> |
| 688 | @ <th1>enable_output 1</th1> |
| 689 | @ |
| 690 | @ <tr> |
| 691 | @ <td align="right"> |
| 692 | @ <input type="submit" name="preview" value="Preview" /> |
| 693 | @ </td> |
| 694 | @ <td align="left">See how the description will appear after formatting.</td> |
| 695 | @ </tr> |
| 696 | @ |
| 697 | @ <th1>enable_output [info exists preview]</th1> |
| 698 | @ <tr> |
| 699 | @ <td align="right"> |
| 700 | @ <input type="submit" name="submit" value="Submit" /> |
| 701 | @ </td> |
| 702 | @ <td align="left">Apply the changes shown above</td> |
| 703 | @ </tr> |
| 704 | @ <th1>enable_output 1</th1> |
| 705 | @ |
| 706 | @ <tr> |
| 707 | @ <td align="right"> |
| 708 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 709 | @ </td> |
| 710 | @ <td>Abandon this edit</td> |
| 711 | @ </tr> |
| 712 | @ |
| 713 | @ </table> |
| @@ -912,36 +911,36 @@ | |
| 912 | style_header("Ticket Display On Timelines"); |
| 913 | db_begin_transaction(); |
| 914 | @ <form action="%R/tktsetup_timeline" method="post"><div> |
| 915 | login_insert_csrf_secret(); |
| 916 | |
| 917 | @ <hr /> |
| 918 | entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", |
| 919 | "title", 0); |
| 920 | @ <p>An SQL expression in a query against the TICKET table that will |
| 921 | @ return the title of the ticket for display purposes. |
| 922 | @ (Property: ticket-title-expr)</p> |
| 923 | |
| 924 | @ <hr /> |
| 925 | entry_attribute("Ticket Status", 40, "ticket-status-column", "s", |
| 926 | "status", 0); |
| 927 | @ <p>The name of the column in the TICKET table that contains the ticket |
| 928 | @ status in human-readable form. Case sensitive. |
| 929 | @ (Property: ticket-status-column)</p> |
| 930 | |
| 931 | @ <hr /> |
| 932 | entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c", |
| 933 | "status='Closed'", 0); |
| 934 | @ <p>An SQL expression that evaluates to true in a TICKET table query if |
| 935 | @ the ticket is closed. |
| 936 | @ (Property: ticket-closed-expr)</p> |
| 937 | |
| 938 | @ <hr /> |
| 939 | @ <p> |
| 940 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 941 | @ <input type="submit" name="setup" value="Cancel" /> |
| 942 | @ </p> |
| 943 | @ </div></form> |
| 944 | db_end_transaction(0); |
| 945 | style_finish_page(); |
| 946 | |
| 947 | } |
| 948 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -156,16 +156,16 @@ | |
| 156 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 157 | login_insert_csrf_secret(); |
| 158 | @ <p>%s(zDesc)</p> |
| 159 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 160 | @ <blockquote><p> |
| 161 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 162 | @ <input type="submit" name="clear" value="Revert To Default"> |
| 163 | @ <input type="submit" name="setup" value="Cancel"> |
| 164 | @ </p></blockquote> |
| 165 | @ </div></form> |
| 166 | @ <hr> |
| 167 | @ <h2>Default %s(zTitle)</h2> |
| 168 | @ <blockquote><pre> |
| 169 | @ %h(zDfltValue) |
| 170 | @ </pre></blockquote> |
| 171 | style_finish_page(); |
| @@ -324,12 +324,12 @@ | |
| 324 | @ </th1> |
| 325 | @ <h1 style="text-align: center;">Enter A New Ticket</h1> |
| 326 | @ <table cellpadding="5"> |
| 327 | @ <tr> |
| 328 | @ <td colspan="3"> |
| 329 | @ Enter a one-line summary of the ticket:<br> |
| 330 | @ <input type="text" name="title" size="60" value="$<title>"> |
| 331 | @ </td> |
| 332 | @ </tr> |
| 333 | @ |
| 334 | @ <tr> |
| 335 | @ <td align="right">Type:</td> |
| @@ -338,11 +338,11 @@ | |
| 338 | @ </tr> |
| 339 | @ |
| 340 | @ <tr> |
| 341 | @ <td align="right">Version:</td> |
| 342 | @ <td align="left"> |
| 343 | @ <input type="text" name="foundin" size="20" value="$<foundin>"> |
| 344 | @ </td> |
| 345 | @ <td align="left">In what version or build number do you observe |
| 346 | @ the problem?</td> |
| 347 | @ </tr> |
| 348 | @ |
| @@ -354,12 +354,11 @@ | |
| 354 | @ </tr> |
| 355 | @ |
| 356 | @ <tr> |
| 357 | @ <td align="right">EMail:</td> |
| 358 | @ <td align="left"> |
| 359 | @ <input name="private_contact" value="$<private_contact>" size="30"> |
| 360 | @ </td> |
| 361 | @ <td align="left"><u>Not publicly visible</u> |
| 362 | @ Used by developers to contact you with questions.</td> |
| 363 | @ </tr> |
| 364 | @ |
| @@ -368,19 +367,19 @@ | |
| 367 | @ Enter a detailed description of the problem. |
| 368 | @ For code defects, be sure to provide details on exactly how |
| 369 | @ the problem can be reproduced. Provide as much detail as |
| 370 | @ possible. Format: |
| 371 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 372 | @ <br> |
| 373 | @ <th1>set nline [linecount $comment 50 10]</th1> |
| 374 | @ <textarea name="icomment" cols="80" rows="$nline" |
| 375 | @ wrap="virtual" class="wikiedit">$<icomment></textarea><br> |
| 376 | @ </tr> |
| 377 | @ |
| 378 | @ <th1>enable_output [info exists preview]</th1> |
| 379 | @ <tr><td colspan="3"> |
| 380 | @ Description Preview:<br><hr> |
| 381 | @ <th1> |
| 382 | @ if {$mutype eq "Wiki"} { |
| 383 | @ wiki $icomment |
| 384 | @ } elseif {$mutype eq "Plain Text"} { |
| 385 | @ set r [randhex] |
| @@ -392,33 +391,33 @@ | |
| 391 | @ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>" |
| 392 | @ } else { |
| 393 | @ wiki "<nowiki>$icomment\n</nowiki>" |
| 394 | @ } |
| 395 | @ </th1> |
| 396 | @ <hr></td></tr> |
| 397 | @ <th1>enable_output 1</th1> |
| 398 | @ |
| 399 | @ <tr> |
| 400 | @ <td><td align="left"> |
| 401 | @ <input type="submit" name="preview" value="Preview"> |
| 402 | @ </td> |
| 403 | @ <td align="left">See how the description will appear after formatting.</td> |
| 404 | @ </tr> |
| 405 | @ |
| 406 | @ <th1>enable_output [info exists preview]</th1> |
| 407 | @ <tr> |
| 408 | @ <td><td align="left"> |
| 409 | @ <input type="submit" name="submit" value="Submit"> |
| 410 | @ </td> |
| 411 | @ <td align="left">After filling in the information above, press this |
| 412 | @ button to create the new ticket</td> |
| 413 | @ </tr> |
| 414 | @ <th1>enable_output 1</th1> |
| 415 | @ |
| 416 | @ <tr> |
| 417 | @ <td><td align="left"> |
| 418 | @ <input type="submit" name="cancel" value="Cancel"> |
| 419 | @ </td> |
| 420 | @ <td>Abandon and forget this ticket</td> |
| 421 | @ </tr> |
| 422 | @ </table> |
| 423 | ; |
| @@ -532,11 +531,11 @@ | |
| 531 | @ mimetype as xmimetype, icomment AS xcomment, |
| 532 | @ username AS xusername |
| 533 | @ FROM ticketchng |
| 534 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 535 | @ if {$seenRow} { |
| 536 | @ html "<hr>\n" |
| 537 | @ } else { |
| 538 | @ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" |
| 539 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 540 | @ set seenRow 1 |
| 541 | @ } |
| @@ -616,11 +615,11 @@ | |
| 615 | @ set preview 1 |
| 616 | @ } |
| 617 | @ </th1> |
| 618 | @ <table cellpadding="5"> |
| 619 | @ <tr><td class="tktDspLabel">Title:</td><td> |
| 620 | @ <input type="text" name="title" value="$<title>" size="60"> |
| 621 | @ </td></tr> |
| 622 | @ |
| 623 | @ <tr><td class="tktDspLabel">Status:</td><td> |
| 624 | @ <th1>combobox status $status_choices 1</th1> |
| 625 | @ </td></tr> |
| @@ -646,30 +645,30 @@ | |
| 645 | @ </td></tr> |
| 646 | @ |
| 647 | @ <th1>enable_output [hascap e]</th1> |
| 648 | @ <tr><td class="tktDspLabel">Contact:</td><td> |
| 649 | @ <input type="text" name="private_contact" size="40" |
| 650 | @ value="$<private_contact>"> |
| 651 | @ </td></tr> |
| 652 | @ <th1>enable_output 1</th1> |
| 653 | @ |
| 654 | @ <tr><td class="tktDspLabel">Version Found In:</td><td> |
| 655 | @ <input type="text" name="foundin" size="50" value="$<foundin>"> |
| 656 | @ </td></tr> |
| 657 | @ |
| 658 | @ <tr><td colspan="2"> |
| 659 | @ Append Remark with format |
| 660 | @ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1> |
| 661 | @ from |
| 662 | @ <input type="text" name="username" value="$<username>" size="30">:<br> |
| 663 | @ <textarea name="icomment" cols="80" rows="15" |
| 664 | @ wrap="virtual" class="wikiedit">$<icomment></textarea> |
| 665 | @ </td></tr> |
| 666 | @ |
| 667 | @ <th1>enable_output [info exists preview]</th1> |
| 668 | @ <tr><td colspan="2"> |
| 669 | @ Description Preview:<br><hr> |
| 670 | @ <th1> |
| 671 | @ if {$mutype eq "Wiki"} { |
| 672 | @ wiki $icomment |
| 673 | @ } elseif {$mutype eq "Plain Text"} { |
| 674 | @ set r [randhex] |
| @@ -681,33 +680,33 @@ | |
| 680 | @ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>" |
| 681 | @ } else { |
| 682 | @ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>" |
| 683 | @ } |
| 684 | @ </th1> |
| 685 | @ <hr> |
| 686 | @ </td></tr> |
| 687 | @ <th1>enable_output 1</th1> |
| 688 | @ |
| 689 | @ <tr> |
| 690 | @ <td align="right"> |
| 691 | @ <input type="submit" name="preview" value="Preview"> |
| 692 | @ </td> |
| 693 | @ <td align="left">See how the description will appear after formatting.</td> |
| 694 | @ </tr> |
| 695 | @ |
| 696 | @ <th1>enable_output [info exists preview]</th1> |
| 697 | @ <tr> |
| 698 | @ <td align="right"> |
| 699 | @ <input type="submit" name="submit" value="Submit"> |
| 700 | @ </td> |
| 701 | @ <td align="left">Apply the changes shown above</td> |
| 702 | @ </tr> |
| 703 | @ <th1>enable_output 1</th1> |
| 704 | @ |
| 705 | @ <tr> |
| 706 | @ <td align="right"> |
| 707 | @ <input type="submit" name="cancel" value="Cancel"> |
| 708 | @ </td> |
| 709 | @ <td>Abandon this edit</td> |
| 710 | @ </tr> |
| 711 | @ |
| 712 | @ </table> |
| @@ -912,36 +911,36 @@ | |
| 911 | style_header("Ticket Display On Timelines"); |
| 912 | db_begin_transaction(); |
| 913 | @ <form action="%R/tktsetup_timeline" method="post"><div> |
| 914 | login_insert_csrf_secret(); |
| 915 | |
| 916 | @ <hr> |
| 917 | entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", |
| 918 | "title", 0); |
| 919 | @ <p>An SQL expression in a query against the TICKET table that will |
| 920 | @ return the title of the ticket for display purposes. |
| 921 | @ (Property: ticket-title-expr)</p> |
| 922 | |
| 923 | @ <hr> |
| 924 | entry_attribute("Ticket Status", 40, "ticket-status-column", "s", |
| 925 | "status", 0); |
| 926 | @ <p>The name of the column in the TICKET table that contains the ticket |
| 927 | @ status in human-readable form. Case sensitive. |
| 928 | @ (Property: ticket-status-column)</p> |
| 929 | |
| 930 | @ <hr> |
| 931 | entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c", |
| 932 | "status='Closed'", 0); |
| 933 | @ <p>An SQL expression that evaluates to true in a TICKET table query if |
| 934 | @ the ticket is closed. |
| 935 | @ (Property: ticket-closed-expr)</p> |
| 936 | |
| 937 | @ <hr> |
| 938 | @ <p> |
| 939 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 940 | @ <input type="submit" name="setup" value="Cancel"> |
| 941 | @ </p> |
| 942 | @ </div></form> |
| 943 | db_end_transaction(0); |
| 944 | style_finish_page(); |
| 945 | |
| 946 | } |
| 947 |
+5
-13
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -652,20 +652,14 @@ | ||
| 652 | 652 | */ |
| 653 | 653 | void ensure_empty_dirs_created(int clearDirTable){ |
| 654 | 654 | char *zEmptyDirs = db_get("empty-dirs", 0); |
| 655 | 655 | if( zEmptyDirs!=0 ){ |
| 656 | 656 | int i; |
| 657 | - Blob dirName; | |
| 658 | - Blob dirsList; | |
| 659 | - | |
| 660 | - zEmptyDirs = fossil_strdup(zEmptyDirs); | |
| 661 | - for(i=0; zEmptyDirs[i]; i++){ | |
| 662 | - if( zEmptyDirs[i]==',' ) zEmptyDirs[i] = ' '; | |
| 663 | - } | |
| 664 | - blob_init(&dirsList, zEmptyDirs, -1); | |
| 665 | - while( blob_token(&dirsList, &dirName) ){ | |
| 666 | - char *zDir = blob_str(&dirName); | |
| 657 | + Glob *pGlob = glob_create(zEmptyDirs); | |
| 658 | + | |
| 659 | + for(i=0; i<pGlob->nPattern; i++){ | |
| 660 | + const char *zDir = pGlob->azPattern[i]; | |
| 667 | 661 | char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir); |
| 668 | 662 | switch( file_isdir(zPath, RepoFILE) ){ |
| 669 | 663 | case 0: { /* doesn't exist */ |
| 670 | 664 | fossil_free(zPath); |
| 671 | 665 | zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir); |
| @@ -688,14 +682,12 @@ | ||
| 688 | 682 | fossil_warning("file %s found, but a directory is required " |
| 689 | 683 | "by empty-dirs setting", zDir); |
| 690 | 684 | } |
| 691 | 685 | } |
| 692 | 686 | fossil_free(zPath); |
| 693 | - blob_reset(&dirName); | |
| 694 | 687 | } |
| 695 | - blob_reset(&dirsList); | |
| 696 | - fossil_free(zEmptyDirs); | |
| 688 | + glob_free(pGlob); | |
| 697 | 689 | } |
| 698 | 690 | } |
| 699 | 691 | |
| 700 | 692 | /* |
| 701 | 693 | ** Get the manifest record for a given revision, or the current check-out if |
| 702 | 694 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -652,20 +652,14 @@ | |
| 652 | */ |
| 653 | void ensure_empty_dirs_created(int clearDirTable){ |
| 654 | char *zEmptyDirs = db_get("empty-dirs", 0); |
| 655 | if( zEmptyDirs!=0 ){ |
| 656 | int i; |
| 657 | Blob dirName; |
| 658 | Blob dirsList; |
| 659 | |
| 660 | zEmptyDirs = fossil_strdup(zEmptyDirs); |
| 661 | for(i=0; zEmptyDirs[i]; i++){ |
| 662 | if( zEmptyDirs[i]==',' ) zEmptyDirs[i] = ' '; |
| 663 | } |
| 664 | blob_init(&dirsList, zEmptyDirs, -1); |
| 665 | while( blob_token(&dirsList, &dirName) ){ |
| 666 | char *zDir = blob_str(&dirName); |
| 667 | char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir); |
| 668 | switch( file_isdir(zPath, RepoFILE) ){ |
| 669 | case 0: { /* doesn't exist */ |
| 670 | fossil_free(zPath); |
| 671 | zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir); |
| @@ -688,14 +682,12 @@ | |
| 688 | fossil_warning("file %s found, but a directory is required " |
| 689 | "by empty-dirs setting", zDir); |
| 690 | } |
| 691 | } |
| 692 | fossil_free(zPath); |
| 693 | blob_reset(&dirName); |
| 694 | } |
| 695 | blob_reset(&dirsList); |
| 696 | fossil_free(zEmptyDirs); |
| 697 | } |
| 698 | } |
| 699 | |
| 700 | /* |
| 701 | ** Get the manifest record for a given revision, or the current check-out if |
| 702 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -652,20 +652,14 @@ | |
| 652 | */ |
| 653 | void ensure_empty_dirs_created(int clearDirTable){ |
| 654 | char *zEmptyDirs = db_get("empty-dirs", 0); |
| 655 | if( zEmptyDirs!=0 ){ |
| 656 | int i; |
| 657 | Glob *pGlob = glob_create(zEmptyDirs); |
| 658 | |
| 659 | for(i=0; i<pGlob->nPattern; i++){ |
| 660 | const char *zDir = pGlob->azPattern[i]; |
| 661 | char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir); |
| 662 | switch( file_isdir(zPath, RepoFILE) ){ |
| 663 | case 0: { /* doesn't exist */ |
| 664 | fossil_free(zPath); |
| 665 | zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir); |
| @@ -688,14 +682,12 @@ | |
| 682 | fossil_warning("file %s found, but a directory is required " |
| 683 | "by empty-dirs setting", zDir); |
| 684 | } |
| 685 | } |
| 686 | fossil_free(zPath); |
| 687 | } |
| 688 | glob_free(pGlob); |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | /* |
| 693 | ** Get the manifest record for a given revision, or the current check-out if |
| 694 |
+1
-1
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -755,11 +755,11 @@ | ||
| 755 | 755 | if( skip>0 || cnt>n ){ |
| 756 | 756 | style_submenu_element("All", "%R/access_log?n=10000000"); |
| 757 | 757 | } |
| 758 | 758 | @ </tbody></table> |
| 759 | 759 | db_finalize(&q); |
| 760 | - @ <hr /> | |
| 760 | + @ <hr> | |
| 761 | 761 | @ <form method="post" action="%R/access_log"> |
| 762 | 762 | @ <label><input type="checkbox" name="delold"> |
| 763 | 763 | @ Delete all but the most recent 200 entries</input></label> |
| 764 | 764 | @ <input type="submit" name="deloldbtn" value="Delete"></input> |
| 765 | 765 | @ </form> |
| 766 | 766 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -755,11 +755,11 @@ | |
| 755 | if( skip>0 || cnt>n ){ |
| 756 | style_submenu_element("All", "%R/access_log?n=10000000"); |
| 757 | } |
| 758 | @ </tbody></table> |
| 759 | db_finalize(&q); |
| 760 | @ <hr /> |
| 761 | @ <form method="post" action="%R/access_log"> |
| 762 | @ <label><input type="checkbox" name="delold"> |
| 763 | @ Delete all but the most recent 200 entries</input></label> |
| 764 | @ <input type="submit" name="deloldbtn" value="Delete"></input> |
| 765 | @ </form> |
| 766 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -755,11 +755,11 @@ | |
| 755 | if( skip>0 || cnt>n ){ |
| 756 | style_submenu_element("All", "%R/access_log?n=10000000"); |
| 757 | } |
| 758 | @ </tbody></table> |
| 759 | db_finalize(&q); |
| 760 | @ <hr> |
| 761 | @ <form method="post" action="%R/access_log"> |
| 762 | @ <label><input type="checkbox" name="delold"> |
| 763 | @ Delete all but the most recent 200 entries</input></label> |
| 764 | @ <input type="submit" name="deloldbtn" value="Delete"></input> |
| 765 | @ </form> |
| 766 |
+3
| --- src/vfile.c | ||
| +++ src/vfile.c | ||
| @@ -255,10 +255,13 @@ | ||
| 255 | 255 | } |
| 256 | 256 | #ifndef _WIN32 |
| 257 | 257 | if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){ |
| 258 | 258 | /* Changing to a symlink takes priority over all other change types. */ |
| 259 | 259 | chnged = 7; |
| 260 | + }else if( origPerm==PERM_LNK && currentPerm!=PERM_LNK ){ | |
| 261 | + /* Ditto, other direction */ | |
| 262 | + chnged = 9; | |
| 260 | 263 | }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){ |
| 261 | 264 | /* Confirm metadata change types. */ |
| 262 | 265 | if( origPerm==currentPerm ){ |
| 263 | 266 | chnged = 0; |
| 264 | 267 | }else if( currentPerm==PERM_EXE ){ |
| 265 | 268 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -255,10 +255,13 @@ | |
| 255 | } |
| 256 | #ifndef _WIN32 |
| 257 | if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){ |
| 258 | /* Changing to a symlink takes priority over all other change types. */ |
| 259 | chnged = 7; |
| 260 | }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){ |
| 261 | /* Confirm metadata change types. */ |
| 262 | if( origPerm==currentPerm ){ |
| 263 | chnged = 0; |
| 264 | }else if( currentPerm==PERM_EXE ){ |
| 265 |
| --- src/vfile.c | |
| +++ src/vfile.c | |
| @@ -255,10 +255,13 @@ | |
| 255 | } |
| 256 | #ifndef _WIN32 |
| 257 | if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){ |
| 258 | /* Changing to a symlink takes priority over all other change types. */ |
| 259 | chnged = 7; |
| 260 | }else if( origPerm==PERM_LNK && currentPerm!=PERM_LNK ){ |
| 261 | /* Ditto, other direction */ |
| 262 | chnged = 9; |
| 263 | }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){ |
| 264 | /* Confirm metadata change types. */ |
| 265 | if( origPerm==currentPerm ){ |
| 266 | chnged = 0; |
| 267 | }else if( currentPerm==PERM_EXE ){ |
| 268 |
+18
-18
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -612,11 +612,11 @@ | ||
| 612 | 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | 613 | blob_reset(&wiki); |
| 614 | 614 | } |
| 615 | 615 | manifest_destroy(pWiki); |
| 616 | 616 | if( !isPopup ){ |
| 617 | - char * zLabel = mprintf("<hr /><h2><a href='%R/attachlist?name=%T'>" | |
| 617 | + char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" | |
| 618 | 618 | "Attachments</a>:</h2><ul>", |
| 619 | 619 | zPageName); |
| 620 | 620 | attachment_list(zPageName, zLabel); |
| 621 | 621 | fossil_free(zLabel); |
| 622 | 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | ||
| 1536 | 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | 1538 | well_formed_wiki_name_rules(); |
| 1539 | 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | 1540 | @ <p>Name of new wiki page: |
| 1541 | - @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> | |
| 1541 | + @ <input style="width: 35;" type="text" name="name" value="%h(zName)"><br> | |
| 1542 | 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | - @ <br /><input type="submit" value="Create" /> | |
| 1544 | + @ <br><input type="submit" value="Create"> | |
| 1545 | 1545 | @ </p></form> |
| 1546 | 1546 | if( zName[0] ){ |
| 1547 | 1547 | @ <p><span class="wikiError"> |
| 1548 | 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | 1549 | } |
| @@ -1563,16 +1563,16 @@ | ||
| 1563 | 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | 1564 | zRemark = PD("r",""); |
| 1565 | 1565 | zUser = PD("u",g.zLogin); |
| 1566 | 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | - blob_appendf(p, "\n\n<hr /><div id=\"%s\"><i>On %s UTC %h", | |
| 1568 | + blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", | |
| 1569 | 1569 | zId, zDate, login_name()); |
| 1570 | 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | 1572 | } |
| 1573 | - blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); | |
| 1573 | + blob_appendf(p, " added:</i><br>\n%s</div id=\"%s\">", zRemark, zId); | |
| 1574 | 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | 1578 | } |
| @@ -1685,31 +1685,31 @@ | ||
| 1685 | 1685 | } |
| 1686 | 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | 1687 | Blob preview; |
| 1688 | 1688 | blob_zero(&preview); |
| 1689 | 1689 | appendRemark(&preview, zMimetype); |
| 1690 | - @ Preview:<hr /> | |
| 1690 | + @ Preview:<hr> | |
| 1691 | 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | - @ <hr /> | |
| 1693 | + @ <hr> | |
| 1694 | 1694 | blob_reset(&preview); |
| 1695 | 1695 | } |
| 1696 | 1696 | zUser = PD("u", g.zLogin); |
| 1697 | 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | 1698 | login_insert_csrf_secret(); |
| 1699 | - @ <input type="hidden" name="name" value="%h(zPageName)" /> | |
| 1700 | - @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> | |
| 1699 | + @ <input type="hidden" name="name" value="%h(zPageName)"> | |
| 1700 | + @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> | |
| 1701 | 1701 | @ Your Name: |
| 1702 | - @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> | |
| 1702 | + @ <input type="text" name="u" size="20" value="%h(zUser)"><br> | |
| 1703 | 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | - @ Comment to append (formatted as %s(zFormat)):<br /> | |
| 1704 | + @ Comment to append (formatted as %s(zFormat)):<br> | |
| 1705 | 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | - @ <br /> | |
| 1708 | - @ <input type="submit" name="preview" value="Preview Your Comment" /> | |
| 1709 | - @ <input type="submit" name="submit" value="Append Your Changes" /> | |
| 1710 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 1707 | + @ <br> | |
| 1708 | + @ <input type="submit" name="preview" value="Preview Your Comment"> | |
| 1709 | + @ <input type="submit" name="submit" value="Append Your Changes"> | |
| 1710 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 1711 | 1711 | captcha_generate(0); |
| 1712 | 1712 | @ </form> |
| 1713 | 1713 | manifest_destroy(pWiki); |
| 1714 | 1714 | style_finish_page(); |
| 1715 | 1715 | } |
| @@ -1750,12 +1750,12 @@ | ||
| 1750 | 1750 | " ORDER BY event.mtime DESC", |
| 1751 | 1751 | zPageName |
| 1752 | 1752 | ); |
| 1753 | 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | - @ <input id="wh-pid" name="pid" type="radio" hidden /> | |
| 1756 | - @ <input id="wh-id" name="id" type="hidden" /> | |
| 1755 | + @ <input id="wh-pid" name="pid" type="radio" hidden> | |
| 1756 | + @ <input id="wh-id" name="id" type="hidden"> | |
| 1757 | 1757 | @ </form> |
| 1758 | 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | 1759 | @ <div class="brlist"> |
| 1760 | 1760 | @ <table> |
| 1761 | 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | ||
| 1790 | 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | 1791 | @ <td>%s(zAge)</td> |
| 1792 | 1792 | fossil_free(zAge); |
| 1793 | 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | - @ <td>%h(zUser)<span class="wh-iterations" hidden /></td> | |
| 1795 | + @ <td>%h(zUser)<span class="wh-iterations" hidden></td> | |
| 1796 | 1796 | if( showRid ){ |
| 1797 | 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | 1798 | } |
| 1799 | 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | 1800 | @ </tr> |
| 1801 | 1801 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -612,11 +612,11 @@ | |
| 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | blob_reset(&wiki); |
| 614 | } |
| 615 | manifest_destroy(pWiki); |
| 616 | if( !isPopup ){ |
| 617 | char * zLabel = mprintf("<hr /><h2><a href='%R/attachlist?name=%T'>" |
| 618 | "Attachments</a>:</h2><ul>", |
| 619 | zPageName); |
| 620 | attachment_list(zPageName, zLabel); |
| 621 | fossil_free(zLabel); |
| 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | |
| 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | well_formed_wiki_name_rules(); |
| 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | @ <p>Name of new wiki page: |
| 1541 | @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> |
| 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | @ <br /><input type="submit" value="Create" /> |
| 1545 | @ </p></form> |
| 1546 | if( zName[0] ){ |
| 1547 | @ <p><span class="wikiError"> |
| 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | } |
| @@ -1563,16 +1563,16 @@ | |
| 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | zRemark = PD("r",""); |
| 1565 | zUser = PD("u",g.zLogin); |
| 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | blob_appendf(p, "\n\n<hr /><div id=\"%s\"><i>On %s UTC %h", |
| 1569 | zId, zDate, login_name()); |
| 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | } |
| 1573 | blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); |
| 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | } |
| @@ -1685,31 +1685,31 @@ | |
| 1685 | } |
| 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | Blob preview; |
| 1688 | blob_zero(&preview); |
| 1689 | appendRemark(&preview, zMimetype); |
| 1690 | @ Preview:<hr /> |
| 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | @ <hr /> |
| 1694 | blob_reset(&preview); |
| 1695 | } |
| 1696 | zUser = PD("u", g.zLogin); |
| 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | login_insert_csrf_secret(); |
| 1699 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 1700 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> |
| 1701 | @ Your Name: |
| 1702 | @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> |
| 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | @ Comment to append (formatted as %s(zFormat)):<br /> |
| 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | @ <br /> |
| 1708 | @ <input type="submit" name="preview" value="Preview Your Comment" /> |
| 1709 | @ <input type="submit" name="submit" value="Append Your Changes" /> |
| 1710 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 1711 | captcha_generate(0); |
| 1712 | @ </form> |
| 1713 | manifest_destroy(pWiki); |
| 1714 | style_finish_page(); |
| 1715 | } |
| @@ -1750,12 +1750,12 @@ | |
| 1750 | " ORDER BY event.mtime DESC", |
| 1751 | zPageName |
| 1752 | ); |
| 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | @ <input id="wh-pid" name="pid" type="radio" hidden /> |
| 1756 | @ <input id="wh-id" name="id" type="hidden" /> |
| 1757 | @ </form> |
| 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | @ <div class="brlist"> |
| 1760 | @ <table> |
| 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | @ <td>%s(zAge)</td> |
| 1792 | fossil_free(zAge); |
| 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | @ <td>%h(zUser)<span class="wh-iterations" hidden /></td> |
| 1796 | if( showRid ){ |
| 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | } |
| 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | @ </tr> |
| 1801 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -612,11 +612,11 @@ | |
| 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | blob_reset(&wiki); |
| 614 | } |
| 615 | manifest_destroy(pWiki); |
| 616 | if( !isPopup ){ |
| 617 | char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" |
| 618 | "Attachments</a>:</h2><ul>", |
| 619 | zPageName); |
| 620 | attachment_list(zPageName, zLabel); |
| 621 | fossil_free(zLabel); |
| 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | |
| 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | well_formed_wiki_name_rules(); |
| 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | @ <p>Name of new wiki page: |
| 1541 | @ <input style="width: 35;" type="text" name="name" value="%h(zName)"><br> |
| 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | @ <br><input type="submit" value="Create"> |
| 1545 | @ </p></form> |
| 1546 | if( zName[0] ){ |
| 1547 | @ <p><span class="wikiError"> |
| 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | } |
| @@ -1563,16 +1563,16 @@ | |
| 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | zRemark = PD("r",""); |
| 1565 | zUser = PD("u",g.zLogin); |
| 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", |
| 1569 | zId, zDate, login_name()); |
| 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | } |
| 1573 | blob_appendf(p, " added:</i><br>\n%s</div id=\"%s\">", zRemark, zId); |
| 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | } |
| @@ -1685,31 +1685,31 @@ | |
| 1685 | } |
| 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | Blob preview; |
| 1688 | blob_zero(&preview); |
| 1689 | appendRemark(&preview, zMimetype); |
| 1690 | @ Preview:<hr> |
| 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | @ <hr> |
| 1694 | blob_reset(&preview); |
| 1695 | } |
| 1696 | zUser = PD("u", g.zLogin); |
| 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | login_insert_csrf_secret(); |
| 1699 | @ <input type="hidden" name="name" value="%h(zPageName)"> |
| 1700 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1701 | @ Your Name: |
| 1702 | @ <input type="text" name="u" size="20" value="%h(zUser)"><br> |
| 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | @ Comment to append (formatted as %s(zFormat)):<br> |
| 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | @ <br> |
| 1708 | @ <input type="submit" name="preview" value="Preview Your Comment"> |
| 1709 | @ <input type="submit" name="submit" value="Append Your Changes"> |
| 1710 | @ <input type="submit" name="cancel" value="Cancel"> |
| 1711 | captcha_generate(0); |
| 1712 | @ </form> |
| 1713 | manifest_destroy(pWiki); |
| 1714 | style_finish_page(); |
| 1715 | } |
| @@ -1750,12 +1750,12 @@ | |
| 1750 | " ORDER BY event.mtime DESC", |
| 1751 | zPageName |
| 1752 | ); |
| 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | @ <input id="wh-pid" name="pid" type="radio" hidden> |
| 1756 | @ <input id="wh-id" name="id" type="hidden"> |
| 1757 | @ </form> |
| 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | @ <div class="brlist"> |
| 1760 | @ <table> |
| 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | @ <td>%s(zAge)</td> |
| 1792 | fossil_free(zAge); |
| 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | @ <td>%h(zUser)<span class="wh-iterations" hidden></td> |
| 1796 | if( showRid ){ |
| 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | } |
| 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | @ </tr> |
| 1801 |
+18
-18
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -612,11 +612,11 @@ | ||
| 612 | 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | 613 | blob_reset(&wiki); |
| 614 | 614 | } |
| 615 | 615 | manifest_destroy(pWiki); |
| 616 | 616 | if( !isPopup ){ |
| 617 | - char * zLabel = mprintf("<hr /><h2><a href='%R/attachlist?name=%T'>" | |
| 617 | + char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" | |
| 618 | 618 | "Attachments</a>:</h2><ul>", |
| 619 | 619 | zPageName); |
| 620 | 620 | attachment_list(zPageName, zLabel); |
| 621 | 621 | fossil_free(zLabel); |
| 622 | 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | ||
| 1536 | 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | 1538 | well_formed_wiki_name_rules(); |
| 1539 | 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | 1540 | @ <p>Name of new wiki page: |
| 1541 | - @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> | |
| 1541 | + @ <input style="width: 35;" type="text" name="name" value="%h(zName)"><br> | |
| 1542 | 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | - @ <br /><input type="submit" value="Create" /> | |
| 1544 | + @ <br><input type="submit" value="Create"> | |
| 1545 | 1545 | @ </p></form> |
| 1546 | 1546 | if( zName[0] ){ |
| 1547 | 1547 | @ <p><span class="wikiError"> |
| 1548 | 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | 1549 | } |
| @@ -1563,16 +1563,16 @@ | ||
| 1563 | 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | 1564 | zRemark = PD("r",""); |
| 1565 | 1565 | zUser = PD("u",g.zLogin); |
| 1566 | 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | - blob_appendf(p, "\n\n<hr /><div id=\"%s\"><i>On %s UTC %h", | |
| 1568 | + blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", | |
| 1569 | 1569 | zId, zDate, login_name()); |
| 1570 | 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | 1572 | } |
| 1573 | - blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); | |
| 1573 | + blob_appendf(p, " added:</i><br>\n%s</div id=\"%s\">", zRemark, zId); | |
| 1574 | 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | 1578 | } |
| @@ -1685,31 +1685,31 @@ | ||
| 1685 | 1685 | } |
| 1686 | 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | 1687 | Blob preview; |
| 1688 | 1688 | blob_zero(&preview); |
| 1689 | 1689 | appendRemark(&preview, zMimetype); |
| 1690 | - @ Preview:<hr /> | |
| 1690 | + @ Preview:<hr> | |
| 1691 | 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | - @ <hr /> | |
| 1693 | + @ <hr> | |
| 1694 | 1694 | blob_reset(&preview); |
| 1695 | 1695 | } |
| 1696 | 1696 | zUser = PD("u", g.zLogin); |
| 1697 | 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | 1698 | login_insert_csrf_secret(); |
| 1699 | - @ <input type="hidden" name="name" value="%h(zPageName)" /> | |
| 1700 | - @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> | |
| 1699 | + @ <input type="hidden" name="name" value="%h(zPageName)"> | |
| 1700 | + @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> | |
| 1701 | 1701 | @ Your Name: |
| 1702 | - @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> | |
| 1702 | + @ <input type="text" name="u" size="20" value="%h(zUser)"><br> | |
| 1703 | 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | - @ Comment to append (formatted as %s(zFormat)):<br /> | |
| 1704 | + @ Comment to append (formatted as %s(zFormat)):<br> | |
| 1705 | 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | - @ <br /> | |
| 1708 | - @ <input type="submit" name="preview" value="Preview Your Comment" /> | |
| 1709 | - @ <input type="submit" name="submit" value="Append Your Changes" /> | |
| 1710 | - @ <input type="submit" name="cancel" value="Cancel" /> | |
| 1707 | + @ <br> | |
| 1708 | + @ <input type="submit" name="preview" value="Preview Your Comment"> | |
| 1709 | + @ <input type="submit" name="submit" value="Append Your Changes"> | |
| 1710 | + @ <input type="submit" name="cancel" value="Cancel"> | |
| 1711 | 1711 | captcha_generate(0); |
| 1712 | 1712 | @ </form> |
| 1713 | 1713 | manifest_destroy(pWiki); |
| 1714 | 1714 | style_finish_page(); |
| 1715 | 1715 | } |
| @@ -1750,12 +1750,12 @@ | ||
| 1750 | 1750 | " ORDER BY event.mtime DESC", |
| 1751 | 1751 | zPageName |
| 1752 | 1752 | ); |
| 1753 | 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | - @ <input id="wh-pid" name="pid" type="radio" hidden /> | |
| 1756 | - @ <input id="wh-id" name="id" type="hidden" /> | |
| 1755 | + @ <input id="wh-pid" name="pid" type="radio" hidden> | |
| 1756 | + @ <input id="wh-id" name="id" type="hidden"> | |
| 1757 | 1757 | @ </form> |
| 1758 | 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | 1759 | @ <div class="brlist"> |
| 1760 | 1760 | @ <table> |
| 1761 | 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | ||
| 1790 | 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | 1791 | @ <td>%s(zAge)</td> |
| 1792 | 1792 | fossil_free(zAge); |
| 1793 | 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | - @ <td>%h(zUser)<span class="wh-iterations" hidden /></td> | |
| 1795 | + @ <td>%h(zUser)<span class="wh-iterations" hidden></td> | |
| 1796 | 1796 | if( showRid ){ |
| 1797 | 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | 1798 | } |
| 1799 | 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | 1800 | @ </tr> |
| 1801 | 1801 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -612,11 +612,11 @@ | |
| 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | blob_reset(&wiki); |
| 614 | } |
| 615 | manifest_destroy(pWiki); |
| 616 | if( !isPopup ){ |
| 617 | char * zLabel = mprintf("<hr /><h2><a href='%R/attachlist?name=%T'>" |
| 618 | "Attachments</a>:</h2><ul>", |
| 619 | zPageName); |
| 620 | attachment_list(zPageName, zLabel); |
| 621 | fossil_free(zLabel); |
| 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | |
| 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | well_formed_wiki_name_rules(); |
| 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | @ <p>Name of new wiki page: |
| 1541 | @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> |
| 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | @ <br /><input type="submit" value="Create" /> |
| 1545 | @ </p></form> |
| 1546 | if( zName[0] ){ |
| 1547 | @ <p><span class="wikiError"> |
| 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | } |
| @@ -1563,16 +1563,16 @@ | |
| 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | zRemark = PD("r",""); |
| 1565 | zUser = PD("u",g.zLogin); |
| 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | blob_appendf(p, "\n\n<hr /><div id=\"%s\"><i>On %s UTC %h", |
| 1569 | zId, zDate, login_name()); |
| 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | } |
| 1573 | blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); |
| 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | } |
| @@ -1685,31 +1685,31 @@ | |
| 1685 | } |
| 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | Blob preview; |
| 1688 | blob_zero(&preview); |
| 1689 | appendRemark(&preview, zMimetype); |
| 1690 | @ Preview:<hr /> |
| 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | @ <hr /> |
| 1694 | blob_reset(&preview); |
| 1695 | } |
| 1696 | zUser = PD("u", g.zLogin); |
| 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | login_insert_csrf_secret(); |
| 1699 | @ <input type="hidden" name="name" value="%h(zPageName)" /> |
| 1700 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> |
| 1701 | @ Your Name: |
| 1702 | @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> |
| 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | @ Comment to append (formatted as %s(zFormat)):<br /> |
| 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | @ <br /> |
| 1708 | @ <input type="submit" name="preview" value="Preview Your Comment" /> |
| 1709 | @ <input type="submit" name="submit" value="Append Your Changes" /> |
| 1710 | @ <input type="submit" name="cancel" value="Cancel" /> |
| 1711 | captcha_generate(0); |
| 1712 | @ </form> |
| 1713 | manifest_destroy(pWiki); |
| 1714 | style_finish_page(); |
| 1715 | } |
| @@ -1750,12 +1750,12 @@ | |
| 1750 | " ORDER BY event.mtime DESC", |
| 1751 | zPageName |
| 1752 | ); |
| 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | @ <input id="wh-pid" name="pid" type="radio" hidden /> |
| 1756 | @ <input id="wh-id" name="id" type="hidden" /> |
| 1757 | @ </form> |
| 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | @ <div class="brlist"> |
| 1760 | @ <table> |
| 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | @ <td>%s(zAge)</td> |
| 1792 | fossil_free(zAge); |
| 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | @ <td>%h(zUser)<span class="wh-iterations" hidden /></td> |
| 1796 | if( showRid ){ |
| 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | } |
| 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | @ </tr> |
| 1801 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -612,11 +612,11 @@ | |
| 612 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 613 | blob_reset(&wiki); |
| 614 | } |
| 615 | manifest_destroy(pWiki); |
| 616 | if( !isPopup ){ |
| 617 | char * zLabel = mprintf("<hr><h2><a href='%R/attachlist?name=%T'>" |
| 618 | "Attachments</a>:</h2><ul>", |
| 619 | zPageName); |
| 620 | attachment_list(zPageName, zLabel); |
| 621 | fossil_free(zLabel); |
| 622 | document_emit_js(/*for optional pikchr support*/); |
| @@ -1536,14 +1536,14 @@ | |
| 1536 | wiki_standard_submenu(W_ALL_BUT(W_NEW)); |
| 1537 | @ <p>Rules for wiki page names:</p> |
| 1538 | well_formed_wiki_name_rules(); |
| 1539 | form_begin(0, "%R/wikinew"); |
| 1540 | @ <p>Name of new wiki page: |
| 1541 | @ <input style="width: 35;" type="text" name="name" value="%h(zName)"><br> |
| 1542 | @ %z(href("%R/markup_help"))Markup style</a>: |
| 1543 | mimetype_option_menu("text/x-markdown", "mimetype"); |
| 1544 | @ <br><input type="submit" value="Create"> |
| 1545 | @ </p></form> |
| 1546 | if( zName[0] ){ |
| 1547 | @ <p><span class="wikiError"> |
| 1548 | @ "%h(zName)" is not a valid wiki page name!</span></p> |
| 1549 | } |
| @@ -1563,16 +1563,16 @@ | |
| 1563 | zDate = db_text(0, "SELECT datetime('now')"); |
| 1564 | zRemark = PD("r",""); |
| 1565 | zUser = PD("u",g.zLogin); |
| 1566 | if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ |
| 1567 | zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); |
| 1568 | blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", |
| 1569 | zId, zDate, login_name()); |
| 1570 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1571 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1572 | } |
| 1573 | blob_appendf(p, " added:</i><br>\n%s</div id=\"%s\">", zRemark, zId); |
| 1574 | }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ |
| 1575 | blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); |
| 1576 | if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ |
| 1577 | blob_appendf(p, " (claiming to be %h)", zUser); |
| 1578 | } |
| @@ -1685,31 +1685,31 @@ | |
| 1685 | } |
| 1686 | if( !isSandbox && P("preview")!=0 ){ |
| 1687 | Blob preview; |
| 1688 | blob_zero(&preview); |
| 1689 | appendRemark(&preview, zMimetype); |
| 1690 | @ Preview:<hr> |
| 1691 | safe_html_context(DOCSRC_WIKI); |
| 1692 | wiki_render_by_mimetype(&preview, zMimetype); |
| 1693 | @ <hr> |
| 1694 | blob_reset(&preview); |
| 1695 | } |
| 1696 | zUser = PD("u", g.zLogin); |
| 1697 | form_begin(0, "%R/wikiappend"); |
| 1698 | login_insert_csrf_secret(); |
| 1699 | @ <input type="hidden" name="name" value="%h(zPageName)"> |
| 1700 | @ <input type="hidden" name="mimetype" value="%h(zMimetype)"> |
| 1701 | @ Your Name: |
| 1702 | @ <input type="text" name="u" size="20" value="%h(zUser)"><br> |
| 1703 | zFormat = mimetype_common_name(zMimetype); |
| 1704 | @ Comment to append (formatted as %s(zFormat)):<br> |
| 1705 | @ <textarea name="r" class="wikiedit" cols="80" |
| 1706 | @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> |
| 1707 | @ <br> |
| 1708 | @ <input type="submit" name="preview" value="Preview Your Comment"> |
| 1709 | @ <input type="submit" name="submit" value="Append Your Changes"> |
| 1710 | @ <input type="submit" name="cancel" value="Cancel"> |
| 1711 | captcha_generate(0); |
| 1712 | @ </form> |
| 1713 | manifest_destroy(pWiki); |
| 1714 | style_finish_page(); |
| 1715 | } |
| @@ -1750,12 +1750,12 @@ | |
| 1750 | " ORDER BY event.mtime DESC", |
| 1751 | zPageName |
| 1752 | ); |
| 1753 | @ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2> |
| 1754 | form_begin( "id='wh-form'", "%R/wdiff" ); |
| 1755 | @ <input id="wh-pid" name="pid" type="radio" hidden> |
| 1756 | @ <input id="wh-id" name="id" type="hidden"> |
| 1757 | @ </form> |
| 1758 | @ <style> .wh-clickable { cursor: pointer; } </style> |
| 1759 | @ <div class="brlist"> |
| 1760 | @ <table> |
| 1761 | @ <thead><tr> |
| @@ -1790,11 +1790,11 @@ | |
| 1790 | /* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */ |
| 1791 | @ <td>%s(zAge)</td> |
| 1792 | fossil_free(zAge); |
| 1793 | @ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td> |
| 1794 | @ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td> |
| 1795 | @ <td>%h(zUser)<span class="wh-iterations" hidden></td> |
| 1796 | if( showRid ){ |
| 1797 | @ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td> |
| 1798 | } |
| 1799 | @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td> |
| 1800 | @ </tr> |
| 1801 |
+12
-10
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -195,10 +195,11 @@ | ||
| 195 | 195 | MARKUP_CODE, |
| 196 | 196 | MARKUP_COL, |
| 197 | 197 | MARKUP_COLGROUP, |
| 198 | 198 | MARKUP_DD, |
| 199 | 199 | MARKUP_DEL, |
| 200 | + MARKUP_DETAILS, | |
| 200 | 201 | MARKUP_DFN, |
| 201 | 202 | MARKUP_DIV, |
| 202 | 203 | MARKUP_DL, |
| 203 | 204 | MARKUP_DT, |
| 204 | 205 | MARKUP_EM, |
| @@ -229,10 +230,11 @@ | ||
| 229 | 230 | MARKUP_SMALL, |
| 230 | 231 | MARKUP_SPAN, |
| 231 | 232 | MARKUP_STRIKE, |
| 232 | 233 | MARKUP_STRONG, |
| 233 | 234 | MARKUP_SUB, |
| 235 | + MARKUP_SUMMARY, | |
| 234 | 236 | MARKUP_SUP, |
| 235 | 237 | MARKUP_TABLE, |
| 236 | 238 | MARKUP_TBODY, |
| 237 | 239 | MARKUP_TD, |
| 238 | 240 | MARKUP_TFOOT, |
| @@ -286,14 +288,13 @@ | ||
| 286 | 288 | AMSK_TITLE}, |
| 287 | 289 | { "abbr", MARKUP_ABBR, MUTYPE_FONT, |
| 288 | 290 | AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE }, |
| 289 | 291 | { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE }, |
| 290 | 292 | { "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK, |
| 291 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 293 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 292 | 294 | { "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK, |
| 293 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 294 | - | |
| 295 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 295 | 296 | { "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE }, |
| 296 | 297 | { "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE }, |
| 297 | 298 | { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 298 | 299 | { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, |
| 299 | 300 | { "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE }, |
| @@ -303,10 +304,12 @@ | ||
| 303 | 304 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE }, |
| 304 | 305 | { "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK, |
| 305 | 306 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE}, |
| 306 | 307 | { "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE }, |
| 307 | 308 | { "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE }, |
| 309 | + { "details", MARKUP_DETAILS, MUTYPE_BLOCK, | |
| 310 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 308 | 311 | { "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE }, |
| 309 | 312 | { "div", MARKUP_DIV, MUTYPE_BLOCK, |
| 310 | 313 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 311 | 314 | { "dl", MARKUP_DL, MUTYPE_LIST, |
| 312 | 315 | AMSK_COMPACT|AMSK_STYLE }, |
| @@ -313,12 +316,11 @@ | ||
| 313 | 316 | { "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE }, |
| 314 | 317 | { "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE }, |
| 315 | 318 | { "font", MARKUP_FONT, MUTYPE_FONT, |
| 316 | 319 | AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE }, |
| 317 | 320 | { "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK, |
| 318 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 319 | - | |
| 321 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 320 | 322 | { "h1", MARKUP_H1, MUTYPE_BLOCK, |
| 321 | 323 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | 324 | { "h2", MARKUP_H2, MUTYPE_BLOCK, |
| 323 | 325 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 324 | 326 | { "h3", MARKUP_H3, MUTYPE_BLOCK, |
| @@ -327,14 +329,12 @@ | ||
| 327 | 329 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 328 | 330 | { "h5", MARKUP_H5, MUTYPE_BLOCK, |
| 329 | 331 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 330 | 332 | { "h6", MARKUP_H6, MUTYPE_BLOCK, |
| 331 | 333 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 332 | - | |
| 333 | 334 | { "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK, |
| 334 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 335 | - | |
| 335 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 336 | 336 | { "hr", MARKUP_HR, MUTYPE_SINGLE, |
| 337 | 337 | AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH| |
| 338 | 338 | AMSK_STYLE|AMSK_CLASS }, |
| 339 | 339 | { "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE }, |
| 340 | 340 | { "img", MARKUP_IMG, MUTYPE_SINGLE, |
| @@ -343,11 +343,11 @@ | ||
| 343 | 343 | { "ins", MARKUP_INS, MUTYPE_FONT, AMSK_STYLE }, |
| 344 | 344 | { "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE }, |
| 345 | 345 | { "li", MARKUP_LI, MUTYPE_LI, |
| 346 | 346 | AMSK_TYPE|AMSK_VALUE|AMSK_STYLE }, |
| 347 | 347 | { "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK, |
| 348 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 348 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 349 | 349 | { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, |
| 350 | 350 | { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, |
| 351 | 351 | { "ol", MARKUP_OL, MUTYPE_LIST, |
| 352 | 352 | AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, |
| 353 | 353 | { "p", MARKUP_P, MUTYPE_BLOCK, |
| @@ -354,17 +354,19 @@ | ||
| 354 | 354 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 355 | 355 | { "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 356 | 356 | { "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE }, |
| 357 | 357 | { "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE }, |
| 358 | 358 | { "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK, |
| 359 | - AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 359 | + AMSK_ID|AMSK_CLASS|AMSK_STYLE }, | |
| 360 | 360 | { "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE }, |
| 361 | 361 | { "span", MARKUP_SPAN, MUTYPE_BLOCK, |
| 362 | 362 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 363 | 363 | { "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE }, |
| 364 | 364 | { "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE }, |
| 365 | 365 | { "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE }, |
| 366 | + { "summary", MARKUP_SUMMARY, MUTYPE_BLOCK, | |
| 367 | + AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, | |
| 366 | 368 | { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, |
| 367 | 369 | { "table", MARKUP_TABLE, MUTYPE_TABLE, |
| 368 | 370 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| |
| 369 | 371 | AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS| |
| 370 | 372 | AMSK_STYLE }, |
| 371 | 373 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -195,10 +195,11 @@ | |
| 195 | MARKUP_CODE, |
| 196 | MARKUP_COL, |
| 197 | MARKUP_COLGROUP, |
| 198 | MARKUP_DD, |
| 199 | MARKUP_DEL, |
| 200 | MARKUP_DFN, |
| 201 | MARKUP_DIV, |
| 202 | MARKUP_DL, |
| 203 | MARKUP_DT, |
| 204 | MARKUP_EM, |
| @@ -229,10 +230,11 @@ | |
| 229 | MARKUP_SMALL, |
| 230 | MARKUP_SPAN, |
| 231 | MARKUP_STRIKE, |
| 232 | MARKUP_STRONG, |
| 233 | MARKUP_SUB, |
| 234 | MARKUP_SUP, |
| 235 | MARKUP_TABLE, |
| 236 | MARKUP_TBODY, |
| 237 | MARKUP_TD, |
| 238 | MARKUP_TFOOT, |
| @@ -286,14 +288,13 @@ | |
| 286 | AMSK_TITLE}, |
| 287 | { "abbr", MARKUP_ABBR, MUTYPE_FONT, |
| 288 | AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE }, |
| 289 | { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE }, |
| 290 | { "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK, |
| 291 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 292 | { "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK, |
| 293 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 294 | |
| 295 | { "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE }, |
| 296 | { "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE }, |
| 297 | { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 298 | { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, |
| 299 | { "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE }, |
| @@ -303,10 +304,12 @@ | |
| 303 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE }, |
| 304 | { "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK, |
| 305 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE}, |
| 306 | { "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE }, |
| 307 | { "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE }, |
| 308 | { "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE }, |
| 309 | { "div", MARKUP_DIV, MUTYPE_BLOCK, |
| 310 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 311 | { "dl", MARKUP_DL, MUTYPE_LIST, |
| 312 | AMSK_COMPACT|AMSK_STYLE }, |
| @@ -313,12 +316,11 @@ | |
| 313 | { "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE }, |
| 314 | { "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE }, |
| 315 | { "font", MARKUP_FONT, MUTYPE_FONT, |
| 316 | AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE }, |
| 317 | { "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK, |
| 318 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 319 | |
| 320 | { "h1", MARKUP_H1, MUTYPE_BLOCK, |
| 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "h2", MARKUP_H2, MUTYPE_BLOCK, |
| 323 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 324 | { "h3", MARKUP_H3, MUTYPE_BLOCK, |
| @@ -327,14 +329,12 @@ | |
| 327 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 328 | { "h5", MARKUP_H5, MUTYPE_BLOCK, |
| 329 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 330 | { "h6", MARKUP_H6, MUTYPE_BLOCK, |
| 331 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 332 | |
| 333 | { "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK, |
| 334 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 335 | |
| 336 | { "hr", MARKUP_HR, MUTYPE_SINGLE, |
| 337 | AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH| |
| 338 | AMSK_STYLE|AMSK_CLASS }, |
| 339 | { "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE }, |
| 340 | { "img", MARKUP_IMG, MUTYPE_SINGLE, |
| @@ -343,11 +343,11 @@ | |
| 343 | { "ins", MARKUP_INS, MUTYPE_FONT, AMSK_STYLE }, |
| 344 | { "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE }, |
| 345 | { "li", MARKUP_LI, MUTYPE_LI, |
| 346 | AMSK_TYPE|AMSK_VALUE|AMSK_STYLE }, |
| 347 | { "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK, |
| 348 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 349 | { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, |
| 350 | { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, |
| 351 | { "ol", MARKUP_OL, MUTYPE_LIST, |
| 352 | AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, |
| 353 | { "p", MARKUP_P, MUTYPE_BLOCK, |
| @@ -354,17 +354,19 @@ | |
| 354 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 355 | { "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 356 | { "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE }, |
| 357 | { "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE }, |
| 358 | { "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK, |
| 359 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 360 | { "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE }, |
| 361 | { "span", MARKUP_SPAN, MUTYPE_BLOCK, |
| 362 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 363 | { "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE }, |
| 364 | { "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE }, |
| 365 | { "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE }, |
| 366 | { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, |
| 367 | { "table", MARKUP_TABLE, MUTYPE_TABLE, |
| 368 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| |
| 369 | AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS| |
| 370 | AMSK_STYLE }, |
| 371 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -195,10 +195,11 @@ | |
| 195 | MARKUP_CODE, |
| 196 | MARKUP_COL, |
| 197 | MARKUP_COLGROUP, |
| 198 | MARKUP_DD, |
| 199 | MARKUP_DEL, |
| 200 | MARKUP_DETAILS, |
| 201 | MARKUP_DFN, |
| 202 | MARKUP_DIV, |
| 203 | MARKUP_DL, |
| 204 | MARKUP_DT, |
| 205 | MARKUP_EM, |
| @@ -229,10 +230,11 @@ | |
| 230 | MARKUP_SMALL, |
| 231 | MARKUP_SPAN, |
| 232 | MARKUP_STRIKE, |
| 233 | MARKUP_STRONG, |
| 234 | MARKUP_SUB, |
| 235 | MARKUP_SUMMARY, |
| 236 | MARKUP_SUP, |
| 237 | MARKUP_TABLE, |
| 238 | MARKUP_TBODY, |
| 239 | MARKUP_TD, |
| 240 | MARKUP_TFOOT, |
| @@ -286,14 +288,13 @@ | |
| 288 | AMSK_TITLE}, |
| 289 | { "abbr", MARKUP_ABBR, MUTYPE_FONT, |
| 290 | AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE }, |
| 291 | { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE }, |
| 292 | { "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK, |
| 293 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 294 | { "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK, |
| 295 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 296 | { "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE }, |
| 297 | { "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE }, |
| 298 | { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 299 | { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, |
| 300 | { "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE }, |
| @@ -303,10 +304,12 @@ | |
| 304 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE }, |
| 305 | { "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK, |
| 306 | AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE}, |
| 307 | { "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE }, |
| 308 | { "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE }, |
| 309 | { "details", MARKUP_DETAILS, MUTYPE_BLOCK, |
| 310 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 311 | { "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE }, |
| 312 | { "div", MARKUP_DIV, MUTYPE_BLOCK, |
| 313 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 314 | { "dl", MARKUP_DL, MUTYPE_LIST, |
| 315 | AMSK_COMPACT|AMSK_STYLE }, |
| @@ -313,12 +316,11 @@ | |
| 316 | { "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE }, |
| 317 | { "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE }, |
| 318 | { "font", MARKUP_FONT, MUTYPE_FONT, |
| 319 | AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE }, |
| 320 | { "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK, |
| 321 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "h1", MARKUP_H1, MUTYPE_BLOCK, |
| 323 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 324 | { "h2", MARKUP_H2, MUTYPE_BLOCK, |
| 325 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 326 | { "h3", MARKUP_H3, MUTYPE_BLOCK, |
| @@ -327,14 +329,12 @@ | |
| 329 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 330 | { "h5", MARKUP_H5, MUTYPE_BLOCK, |
| 331 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 332 | { "h6", MARKUP_H6, MUTYPE_BLOCK, |
| 333 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 334 | { "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK, |
| 335 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 336 | { "hr", MARKUP_HR, MUTYPE_SINGLE, |
| 337 | AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH| |
| 338 | AMSK_STYLE|AMSK_CLASS }, |
| 339 | { "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE }, |
| 340 | { "img", MARKUP_IMG, MUTYPE_SINGLE, |
| @@ -343,11 +343,11 @@ | |
| 343 | { "ins", MARKUP_INS, MUTYPE_FONT, AMSK_STYLE }, |
| 344 | { "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE }, |
| 345 | { "li", MARKUP_LI, MUTYPE_LI, |
| 346 | AMSK_TYPE|AMSK_VALUE|AMSK_STYLE }, |
| 347 | { "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK, |
| 348 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 349 | { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, |
| 350 | { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, |
| 351 | { "ol", MARKUP_OL, MUTYPE_LIST, |
| 352 | AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, |
| 353 | { "p", MARKUP_P, MUTYPE_BLOCK, |
| @@ -354,17 +354,19 @@ | |
| 354 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 355 | { "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE }, |
| 356 | { "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE }, |
| 357 | { "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE }, |
| 358 | { "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK, |
| 359 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, |
| 360 | { "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE }, |
| 361 | { "span", MARKUP_SPAN, MUTYPE_BLOCK, |
| 362 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 363 | { "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE }, |
| 364 | { "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE }, |
| 365 | { "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE }, |
| 366 | { "summary", MARKUP_SUMMARY, MUTYPE_BLOCK, |
| 367 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 368 | { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, |
| 369 | { "table", MARKUP_TABLE, MUTYPE_TABLE, |
| 370 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| |
| 371 | AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS| |
| 372 | AMSK_STYLE }, |
| 373 |
+1
-1
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -625,11 +625,11 @@ | ||
| 625 | 625 | blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0); |
| 626 | 626 | } |
| 627 | 627 | #if USE_SEE |
| 628 | 628 | zSavedKey = db_get_saved_encryption_key(); |
| 629 | 629 | savedKeySize = db_get_saved_encryption_key_size(); |
| 630 | - if( zSavedKey!=0 && savedKeySize>0 ){ | |
| 630 | + if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){ | |
| 631 | 631 | blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), |
| 632 | 632 | zSavedKey, savedKeySize); |
| 633 | 633 | } |
| 634 | 634 | #endif |
| 635 | 635 | if( WSAStartup(MAKEWORD(2,0), &wd) ){ |
| 636 | 636 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0); |
| 626 | } |
| 627 | #if USE_SEE |
| 628 | zSavedKey = db_get_saved_encryption_key(); |
| 629 | savedKeySize = db_get_saved_encryption_key_size(); |
| 630 | if( zSavedKey!=0 && savedKeySize>0 ){ |
| 631 | blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), |
| 632 | zSavedKey, savedKeySize); |
| 633 | } |
| 634 | #endif |
| 635 | if( WSAStartup(MAKEWORD(2,0), &wd) ){ |
| 636 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -625,11 +625,11 @@ | |
| 625 | blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0); |
| 626 | } |
| 627 | #if USE_SEE |
| 628 | zSavedKey = db_get_saved_encryption_key(); |
| 629 | savedKeySize = db_get_saved_encryption_key_size(); |
| 630 | if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){ |
| 631 | blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), |
| 632 | zSavedKey, savedKeySize); |
| 633 | } |
| 634 | #endif |
| 635 | if( WSAStartup(MAKEWORD(2,0), &wd) ){ |
| 636 |
+13
-3
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -2857,13 +2857,23 @@ | ||
| 2857 | 2857 | g.clockSkewSeen = 1; |
| 2858 | 2858 | } |
| 2859 | 2859 | |
| 2860 | 2860 | fossil_force_newline(); |
| 2861 | 2861 | if( g.zHttpCmd==0 ){ |
| 2862 | - fossil_print( | |
| 2863 | - "%s done, wire bytes sent: %lld received: %lld remote: %s\n", | |
| 2864 | - zOpType, nSent, nRcvd, g.zIpAddr); | |
| 2862 | + if( syncFlags & SYNC_VERBOSE ){ | |
| 2863 | + fossil_print( | |
| 2864 | + "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", | |
| 2865 | + zOpType, nSent, nRcvd, | |
| 2866 | + (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", | |
| 2867 | + (g.zIpAddr && g.zIpAddr[0]!='\0' | |
| 2868 | + && fossil_strcmp(g.zIpAddr, g.url.name)) | |
| 2869 | + ? mprintf(" (%s)", g.zIpAddr) : ""); | |
| 2870 | + }else{ | |
| 2871 | + fossil_print( | |
| 2872 | + "%s done, wire bytes sent: %lld received: %lld remote: %s\n", | |
| 2873 | + zOpType, nSent, nRcvd, g.zIpAddr); | |
| 2874 | + } | |
| 2865 | 2875 | } |
| 2866 | 2876 | if( syncFlags & SYNC_VERBOSE ){ |
| 2867 | 2877 | fossil_print( |
| 2868 | 2878 | "Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd); |
| 2869 | 2879 | } |
| 2870 | 2880 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -2857,13 +2857,23 @@ | |
| 2857 | g.clockSkewSeen = 1; |
| 2858 | } |
| 2859 | |
| 2860 | fossil_force_newline(); |
| 2861 | if( g.zHttpCmd==0 ){ |
| 2862 | fossil_print( |
| 2863 | "%s done, wire bytes sent: %lld received: %lld remote: %s\n", |
| 2864 | zOpType, nSent, nRcvd, g.zIpAddr); |
| 2865 | } |
| 2866 | if( syncFlags & SYNC_VERBOSE ){ |
| 2867 | fossil_print( |
| 2868 | "Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd); |
| 2869 | } |
| 2870 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -2857,13 +2857,23 @@ | |
| 2857 | g.clockSkewSeen = 1; |
| 2858 | } |
| 2859 | |
| 2860 | fossil_force_newline(); |
| 2861 | if( g.zHttpCmd==0 ){ |
| 2862 | if( syncFlags & SYNC_VERBOSE ){ |
| 2863 | fossil_print( |
| 2864 | "%s done, wire bytes sent: %lld received: %lld remote: %s%s\n", |
| 2865 | zOpType, nSent, nRcvd, |
| 2866 | (g.url.name && g.url.name[0]!='\0') ? g.url.name : "", |
| 2867 | (g.zIpAddr && g.zIpAddr[0]!='\0' |
| 2868 | && fossil_strcmp(g.zIpAddr, g.url.name)) |
| 2869 | ? mprintf(" (%s)", g.zIpAddr) : ""); |
| 2870 | }else{ |
| 2871 | fossil_print( |
| 2872 | "%s done, wire bytes sent: %lld received: %lld remote: %s\n", |
| 2873 | zOpType, nSent, nRcvd, g.zIpAddr); |
| 2874 | } |
| 2875 | } |
| 2876 | if( syncFlags & SYNC_VERBOSE ){ |
| 2877 | fossil_print( |
| 2878 | "Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd); |
| 2879 | } |
| 2880 |
+6
-6
| --- src/xfersetup.c | ||
| +++ src/xfersetup.c | ||
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | zButton = "Synchronize"; |
| 62 | 62 | zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", |
| 63 | 63 | g.url.canonical); |
| 64 | 64 | } |
| 65 | 65 | @ <p>Press the <strong>%h(zButton)</strong> button below to |
| 66 | - @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br /> | |
| 66 | + @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br> | |
| 67 | 67 | @ This may be useful when testing the various transfer scripts.</p> |
| 68 | 68 | @ <p>You can use the <code>http -async</code> command in your scripts, but |
| 69 | 69 | @ make sure the <code>th1-uri-regexp</code> setting is set first.</p> |
| 70 | 70 | if( zWarning ){ |
| 71 | 71 | @ |
| @@ -73,11 +73,11 @@ | ||
| 73 | 73 | free(zWarning); |
| 74 | 74 | } |
| 75 | 75 | @ |
| 76 | 76 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 77 | 77 | login_insert_csrf_secret(); |
| 78 | - @ <input type="submit" name="sync" value="%h(zButton)" /> | |
| 78 | + @ <input type="submit" name="sync" value="%h(zButton)"> | |
| 79 | 79 | @ </div></form> |
| 80 | 80 | @ |
| 81 | 81 | if( P("sync") ){ |
| 82 | 82 | user_select(); |
| 83 | 83 | url_enable_proxy(0); |
| @@ -139,17 +139,17 @@ | ||
| 139 | 139 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 140 | 140 | login_insert_csrf_secret(); |
| 141 | 141 | @ <p>%s(zDesc)</p> |
| 142 | 142 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 143 | 143 | @ <p> |
| 144 | - @ <input type="submit" name="submit" value="Apply Changes" /> | |
| 145 | - @ <input type="submit" name="clear" value="Revert To Default" /> | |
| 146 | - @ <input type="submit" name="setup" value="Cancel" /> | |
| 144 | + @ <input type="submit" name="submit" value="Apply Changes"> | |
| 145 | + @ <input type="submit" name="clear" value="Revert To Default"> | |
| 146 | + @ <input type="submit" name="setup" value="Cancel"> | |
| 147 | 147 | @ </p> |
| 148 | 148 | @ </div></form> |
| 149 | 149 | if ( zDfltValue ){ |
| 150 | - @ <hr /> | |
| 150 | + @ <hr> | |
| 151 | 151 | @ <h2>Default %s(zTitle)</h2> |
| 152 | 152 | @ <blockquote><pre> |
| 153 | 153 | @ %h(zDfltValue) |
| 154 | 154 | @ </pre></blockquote> |
| 155 | 155 | } |
| 156 | 156 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | zButton = "Synchronize"; |
| 62 | zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", |
| 63 | g.url.canonical); |
| 64 | } |
| 65 | @ <p>Press the <strong>%h(zButton)</strong> button below to |
| 66 | @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br /> |
| 67 | @ This may be useful when testing the various transfer scripts.</p> |
| 68 | @ <p>You can use the <code>http -async</code> command in your scripts, but |
| 69 | @ make sure the <code>th1-uri-regexp</code> setting is set first.</p> |
| 70 | if( zWarning ){ |
| 71 | @ |
| @@ -73,11 +73,11 @@ | |
| 73 | free(zWarning); |
| 74 | } |
| 75 | @ |
| 76 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 77 | login_insert_csrf_secret(); |
| 78 | @ <input type="submit" name="sync" value="%h(zButton)" /> |
| 79 | @ </div></form> |
| 80 | @ |
| 81 | if( P("sync") ){ |
| 82 | user_select(); |
| 83 | url_enable_proxy(0); |
| @@ -139,17 +139,17 @@ | |
| 139 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 140 | login_insert_csrf_secret(); |
| 141 | @ <p>%s(zDesc)</p> |
| 142 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 143 | @ <p> |
| 144 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 145 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 146 | @ <input type="submit" name="setup" value="Cancel" /> |
| 147 | @ </p> |
| 148 | @ </div></form> |
| 149 | if ( zDfltValue ){ |
| 150 | @ <hr /> |
| 151 | @ <h2>Default %s(zTitle)</h2> |
| 152 | @ <blockquote><pre> |
| 153 | @ %h(zDfltValue) |
| 154 | @ </pre></blockquote> |
| 155 | } |
| 156 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | zButton = "Synchronize"; |
| 62 | zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", |
| 63 | g.url.canonical); |
| 64 | } |
| 65 | @ <p>Press the <strong>%h(zButton)</strong> button below to |
| 66 | @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br> |
| 67 | @ This may be useful when testing the various transfer scripts.</p> |
| 68 | @ <p>You can use the <code>http -async</code> command in your scripts, but |
| 69 | @ make sure the <code>th1-uri-regexp</code> setting is set first.</p> |
| 70 | if( zWarning ){ |
| 71 | @ |
| @@ -73,11 +73,11 @@ | |
| 73 | free(zWarning); |
| 74 | } |
| 75 | @ |
| 76 | @ <form method="post" action="%R/%s(g.zPath)"><div> |
| 77 | login_insert_csrf_secret(); |
| 78 | @ <input type="submit" name="sync" value="%h(zButton)"> |
| 79 | @ </div></form> |
| 80 | @ |
| 81 | if( P("sync") ){ |
| 82 | user_select(); |
| 83 | url_enable_proxy(0); |
| @@ -139,17 +139,17 @@ | |
| 139 | @ <form action="%R/%s(g.zPath)" method="post"><div> |
| 140 | login_insert_csrf_secret(); |
| 141 | @ <p>%s(zDesc)</p> |
| 142 | @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> |
| 143 | @ <p> |
| 144 | @ <input type="submit" name="submit" value="Apply Changes"> |
| 145 | @ <input type="submit" name="clear" value="Revert To Default"> |
| 146 | @ <input type="submit" name="setup" value="Cancel"> |
| 147 | @ </p> |
| 148 | @ </div></form> |
| 149 | if ( zDfltValue ){ |
| 150 | @ <hr> |
| 151 | @ <h2>Default %s(zTitle)</h2> |
| 152 | @ <blockquote><pre> |
| 153 | @ %h(zDfltValue) |
| 154 | @ </pre></blockquote> |
| 155 | } |
| 156 |
+5
-5
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -991,17 +991,17 @@ | ||
| 991 | 991 | etag_check(ETAG_HASH, zKey); |
| 992 | 992 | |
| 993 | 993 | style_set_current_feature("zip"); |
| 994 | 994 | if( P("debug")!=0 ){ |
| 995 | 995 | style_header("%s Archive Generator Debug Screen", zType); |
| 996 | - @ zName = "%h(zName)"<br /> | |
| 997 | - @ rid = %d(rid)<br /> | |
| 996 | + @ zName = "%h(zName)"<br> | |
| 997 | + @ rid = %d(rid)<br> | |
| 998 | 998 | if( zInclude ){ |
| 999 | - @ zInclude = "%h(zInclude)"<br /> | |
| 999 | + @ zInclude = "%h(zInclude)"<br> | |
| 1000 | 1000 | } |
| 1001 | 1001 | if( zExclude ){ |
| 1002 | - @ zExclude = "%h(zExclude)"<br /> | |
| 1002 | + @ zExclude = "%h(zExclude)"<br> | |
| 1003 | 1003 | } |
| 1004 | 1004 | @ zKey = "%h(zKey)" |
| 1005 | 1005 | style_finish_page(); |
| 1006 | 1006 | return; |
| 1007 | 1007 | } |
| @@ -1009,11 +1009,11 @@ | ||
| 1009 | 1009 | style_header("%s Archive Download", zType); |
| 1010 | 1010 | @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'> |
| 1011 | 1011 | cgi_query_parameters_to_hidden(); |
| 1012 | 1012 | @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b> |
| 1013 | 1013 | @ holding the content of check-in <b>%h(zRid)</b>: |
| 1014 | - @ <input type="submit" value="Download" /> | |
| 1014 | + @ <input type="submit" value="Download"> | |
| 1015 | 1015 | @ </form> |
| 1016 | 1016 | style_finish_page(); |
| 1017 | 1017 | return; |
| 1018 | 1018 | } |
| 1019 | 1019 | blob_zero(&zip); |
| 1020 | 1020 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -991,17 +991,17 @@ | |
| 991 | etag_check(ETAG_HASH, zKey); |
| 992 | |
| 993 | style_set_current_feature("zip"); |
| 994 | if( P("debug")!=0 ){ |
| 995 | style_header("%s Archive Generator Debug Screen", zType); |
| 996 | @ zName = "%h(zName)"<br /> |
| 997 | @ rid = %d(rid)<br /> |
| 998 | if( zInclude ){ |
| 999 | @ zInclude = "%h(zInclude)"<br /> |
| 1000 | } |
| 1001 | if( zExclude ){ |
| 1002 | @ zExclude = "%h(zExclude)"<br /> |
| 1003 | } |
| 1004 | @ zKey = "%h(zKey)" |
| 1005 | style_finish_page(); |
| 1006 | return; |
| 1007 | } |
| @@ -1009,11 +1009,11 @@ | |
| 1009 | style_header("%s Archive Download", zType); |
| 1010 | @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'> |
| 1011 | cgi_query_parameters_to_hidden(); |
| 1012 | @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b> |
| 1013 | @ holding the content of check-in <b>%h(zRid)</b>: |
| 1014 | @ <input type="submit" value="Download" /> |
| 1015 | @ </form> |
| 1016 | style_finish_page(); |
| 1017 | return; |
| 1018 | } |
| 1019 | blob_zero(&zip); |
| 1020 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -991,17 +991,17 @@ | |
| 991 | etag_check(ETAG_HASH, zKey); |
| 992 | |
| 993 | style_set_current_feature("zip"); |
| 994 | if( P("debug")!=0 ){ |
| 995 | style_header("%s Archive Generator Debug Screen", zType); |
| 996 | @ zName = "%h(zName)"<br> |
| 997 | @ rid = %d(rid)<br> |
| 998 | if( zInclude ){ |
| 999 | @ zInclude = "%h(zInclude)"<br> |
| 1000 | } |
| 1001 | if( zExclude ){ |
| 1002 | @ zExclude = "%h(zExclude)"<br> |
| 1003 | } |
| 1004 | @ zKey = "%h(zKey)" |
| 1005 | style_finish_page(); |
| 1006 | return; |
| 1007 | } |
| @@ -1009,11 +1009,11 @@ | |
| 1009 | style_header("%s Archive Download", zType); |
| 1010 | @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'> |
| 1011 | cgi_query_parameters_to_hidden(); |
| 1012 | @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b> |
| 1013 | @ holding the content of check-in <b>%h(zRid)</b>: |
| 1014 | @ <input type="submit" value="Download"> |
| 1015 | @ </form> |
| 1016 | style_finish_page(); |
| 1017 | return; |
| 1018 | } |
| 1019 | blob_zero(&zip); |
| 1020 |
+15
-9
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -556,24 +556,24 @@ | ||
| 556 | 556 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" |
| 557 | 557 | |
| 558 | 558 | writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" |
| 559 | 559 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" |
| 560 | 560 | |
| 561 | -writeln { | |
| 561 | +writeln [string map [list <<<NEXT_LINE>>> \\] { | |
| 562 | 562 | $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c |
| 563 | 563 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 564 | 564 | |
| 565 | 565 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 566 | 566 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 567 | 567 | |
| 568 | 568 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 569 | - $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ | |
| 570 | - -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ | |
| 571 | - -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ | |
| 572 | - -sENVIRONMENT=web \ | |
| 573 | - -sMODULARIZE \ | |
| 574 | - -sEXPORT_NAME=initPikchrModule \ | |
| 569 | + $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> | |
| 570 | + -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>> | |
| 571 | + -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> | |
| 572 | + -sENVIRONMENT=web <<<NEXT_LINE>>> | |
| 573 | + -sMODULARIZE <<<NEXT_LINE>>> | |
| 574 | + -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> | |
| 575 | 575 | --minify 0 |
| 576 | 576 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 577 | 577 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 578 | 578 | |
| 579 | 579 | # |
| @@ -580,11 +580,11 @@ | ||
| 580 | 580 | # The list of all the targets that do not correspond to real files. This stops |
| 581 | 581 | # 'make' from getting confused when someone makes an error in a rule. |
| 582 | 582 | # |
| 583 | 583 | |
| 584 | 584 | .PHONY: all install test clean |
| 585 | -} | |
| 585 | +}] | |
| 586 | 586 | |
| 587 | 587 | close $output_file |
| 588 | 588 | # |
| 589 | 589 | # End of the main.mk output |
| 590 | 590 | ############################################################################## |
| @@ -2049,13 +2049,19 @@ | ||
| 2049 | 2049 | foreach s [lsort $extra_files] { |
| 2050 | 2050 | writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@" |
| 2051 | 2051 | set redir {>>} |
| 2052 | 2052 | } |
| 2053 | 2053 | |
| 2054 | +foreach s [lsort $src] { | |
| 2055 | + set extra_h($s) {} | |
| 2056 | +} | |
| 2057 | +set extra_h(builtin) " \"\$(OX)\\builtin_data.h\"" | |
| 2058 | +set extra_h(dispatch) " \"\$(OX)\\page_index.h\"" | |
| 2059 | + | |
| 2054 | 2060 | writeln "" |
| 2055 | 2061 | foreach s [lsort $src] { |
| 2056 | - writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"" | |
| 2062 | + writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"$extra_h($s)" | |
| 2057 | 2063 | writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n" |
| 2058 | 2064 | writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\"" |
| 2059 | 2065 | writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n" |
| 2060 | 2066 | } |
| 2061 | 2067 | |
| 2062 | 2068 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -556,24 +556,24 @@ | |
| 556 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" |
| 557 | |
| 558 | writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" |
| 559 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" |
| 560 | |
| 561 | writeln { |
| 562 | $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c |
| 563 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 564 | |
| 565 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 566 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 567 | |
| 568 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 569 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 570 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ |
| 571 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ |
| 572 | -sENVIRONMENT=web \ |
| 573 | -sMODULARIZE \ |
| 574 | -sEXPORT_NAME=initPikchrModule \ |
| 575 | --minify 0 |
| 576 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 577 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 578 | |
| 579 | # |
| @@ -580,11 +580,11 @@ | |
| 580 | # The list of all the targets that do not correspond to real files. This stops |
| 581 | # 'make' from getting confused when someone makes an error in a rule. |
| 582 | # |
| 583 | |
| 584 | .PHONY: all install test clean |
| 585 | } |
| 586 | |
| 587 | close $output_file |
| 588 | # |
| 589 | # End of the main.mk output |
| 590 | ############################################################################## |
| @@ -2049,13 +2049,19 @@ | |
| 2049 | foreach s [lsort $extra_files] { |
| 2050 | writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@" |
| 2051 | set redir {>>} |
| 2052 | } |
| 2053 | |
| 2054 | writeln "" |
| 2055 | foreach s [lsort $src] { |
| 2056 | writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"" |
| 2057 | writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n" |
| 2058 | writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\"" |
| 2059 | writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n" |
| 2060 | } |
| 2061 | |
| 2062 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -556,24 +556,24 @@ | |
| 556 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" |
| 557 | |
| 558 | writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" |
| 559 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" |
| 560 | |
| 561 | writeln [string map [list <<<NEXT_LINE>>> \\] { |
| 562 | $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c |
| 563 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 564 | |
| 565 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 566 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 567 | |
| 568 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 569 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> |
| 570 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>> |
| 571 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> |
| 572 | -sENVIRONMENT=web <<<NEXT_LINE>>> |
| 573 | -sMODULARIZE <<<NEXT_LINE>>> |
| 574 | -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> |
| 575 | --minify 0 |
| 576 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 577 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 578 | |
| 579 | # |
| @@ -580,11 +580,11 @@ | |
| 580 | # The list of all the targets that do not correspond to real files. This stops |
| 581 | # 'make' from getting confused when someone makes an error in a rule. |
| 582 | # |
| 583 | |
| 584 | .PHONY: all install test clean |
| 585 | }] |
| 586 | |
| 587 | close $output_file |
| 588 | # |
| 589 | # End of the main.mk output |
| 590 | ############################################################################## |
| @@ -2049,13 +2049,19 @@ | |
| 2049 | foreach s [lsort $extra_files] { |
| 2050 | writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@" |
| 2051 | set redir {>>} |
| 2052 | } |
| 2053 | |
| 2054 | foreach s [lsort $src] { |
| 2055 | set extra_h($s) {} |
| 2056 | } |
| 2057 | set extra_h(builtin) " \"\$(OX)\\builtin_data.h\"" |
| 2058 | set extra_h(dispatch) " \"\$(OX)\\page_index.h\"" |
| 2059 | |
| 2060 | writeln "" |
| 2061 | foreach s [lsort $src] { |
| 2062 | writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"$extra_h($s)" |
| 2063 | writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n" |
| 2064 | writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\"" |
| 2065 | writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n" |
| 2066 | } |
| 2067 | |
| 2068 |
+1
| --- tools/mkindex.c | ||
| +++ tools/mkindex.c | ||
| @@ -59,10 +59,11 @@ | ||
| 59 | 59 | ** New arguments may be added in future releases that set additional |
| 60 | 60 | ** bits in the eCmdFlags field. |
| 61 | 61 | ** |
| 62 | 62 | ** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING: |
| 63 | 63 | ** become the built-in help text for that command or webpage or setting. |
| 64 | +** Backslashes must be escaped ("\\" in comment yields "\" in the help text.) | |
| 64 | 65 | ** |
| 65 | 66 | ** Multiple COMMAND: entries can be attached to the same command, thus |
| 66 | 67 | ** creating multiple aliases for that command. Similarly, multiple |
| 67 | 68 | ** WEBPAGE: entries can be attached to the same webpage function, to give |
| 68 | 69 | ** that page aliases. |
| 69 | 70 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -59,10 +59,11 @@ | |
| 59 | ** New arguments may be added in future releases that set additional |
| 60 | ** bits in the eCmdFlags field. |
| 61 | ** |
| 62 | ** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING: |
| 63 | ** become the built-in help text for that command or webpage or setting. |
| 64 | ** |
| 65 | ** Multiple COMMAND: entries can be attached to the same command, thus |
| 66 | ** creating multiple aliases for that command. Similarly, multiple |
| 67 | ** WEBPAGE: entries can be attached to the same webpage function, to give |
| 68 | ** that page aliases. |
| 69 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -59,10 +59,11 @@ | |
| 59 | ** New arguments may be added in future releases that set additional |
| 60 | ** bits in the eCmdFlags field. |
| 61 | ** |
| 62 | ** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING: |
| 63 | ** become the built-in help text for that command or webpage or setting. |
| 64 | ** Backslashes must be escaped ("\\" in comment yields "\" in the help text.) |
| 65 | ** |
| 66 | ** Multiple COMMAND: entries can be attached to the same command, thus |
| 67 | ** creating multiple aliases for that command. Similarly, multiple |
| 68 | ** WEBPAGE: entries can be attached to the same webpage function, to give |
| 69 | ** that page aliases. |
| 70 |
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -565,14 +565,10 @@ | ||
| 565 | 565 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 566 | 566 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 567 | 567 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 568 | 568 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 569 | 569 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 570 | - $(SRCDIR)/../skins/bootstrap/css.txt \ | |
| 571 | - $(SRCDIR)/../skins/bootstrap/details.txt \ | |
| 572 | - $(SRCDIR)/../skins/bootstrap/footer.txt \ | |
| 573 | - $(SRCDIR)/../skins/bootstrap/header.txt \ | |
| 574 | 570 | $(SRCDIR)/../skins/darkmode/css.txt \ |
| 575 | 571 | $(SRCDIR)/../skins/darkmode/details.txt \ |
| 576 | 572 | $(SRCDIR)/../skins/darkmode/footer.txt \ |
| 577 | 573 | $(SRCDIR)/../skins/darkmode/header.txt \ |
| 578 | 574 | $(SRCDIR)/../skins/default/css.txt \ |
| 579 | 575 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -565,14 +565,10 @@ | |
| 565 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 566 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 567 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 568 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 569 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 570 | $(SRCDIR)/../skins/bootstrap/css.txt \ |
| 571 | $(SRCDIR)/../skins/bootstrap/details.txt \ |
| 572 | $(SRCDIR)/../skins/bootstrap/footer.txt \ |
| 573 | $(SRCDIR)/../skins/bootstrap/header.txt \ |
| 574 | $(SRCDIR)/../skins/darkmode/css.txt \ |
| 575 | $(SRCDIR)/../skins/darkmode/details.txt \ |
| 576 | $(SRCDIR)/../skins/darkmode/footer.txt \ |
| 577 | $(SRCDIR)/../skins/darkmode/header.txt \ |
| 578 | $(SRCDIR)/../skins/default/css.txt \ |
| 579 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -565,14 +565,10 @@ | |
| 565 | $(SRCDIR)/../skins/blitz/css.txt \ |
| 566 | $(SRCDIR)/../skins/blitz/details.txt \ |
| 567 | $(SRCDIR)/../skins/blitz/footer.txt \ |
| 568 | $(SRCDIR)/../skins/blitz/header.txt \ |
| 569 | $(SRCDIR)/../skins/blitz/ticket.txt \ |
| 570 | $(SRCDIR)/../skins/darkmode/css.txt \ |
| 571 | $(SRCDIR)/../skins/darkmode/details.txt \ |
| 572 | $(SRCDIR)/../skins/darkmode/footer.txt \ |
| 573 | $(SRCDIR)/../skins/darkmode/header.txt \ |
| 574 | $(SRCDIR)/../skins/default/css.txt \ |
| 575 |
+2
-2
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -1313,11 +1313,11 @@ | ||
| 1313 | 1313 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" |
| 1314 | 1314 | |
| 1315 | 1315 | "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" |
| 1316 | 1316 | "$(OBJDIR)\translate$E" $** > $@ |
| 1317 | 1317 | |
| 1318 | -"$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" | |
| 1318 | +"$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h" | |
| 1319 | 1319 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c" |
| 1320 | 1320 | |
| 1321 | 1321 | "$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c" |
| 1322 | 1322 | "$(OBJDIR)\translate$E" $** > $@ |
| 1323 | 1323 | |
| @@ -1451,11 +1451,11 @@ | ||
| 1451 | 1451 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" |
| 1452 | 1452 | |
| 1453 | 1453 | "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" |
| 1454 | 1454 | "$(OBJDIR)\translate$E" $** > $@ |
| 1455 | 1455 | |
| 1456 | -"$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" | |
| 1456 | +"$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h" | |
| 1457 | 1457 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c" |
| 1458 | 1458 | |
| 1459 | 1459 | "$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c" |
| 1460 | 1460 | "$(OBJDIR)\translate$E" $** > $@ |
| 1461 | 1461 | |
| 1462 | 1462 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -1313,11 +1313,11 @@ | |
| 1313 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" |
| 1314 | |
| 1315 | "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" |
| 1316 | "$(OBJDIR)\translate$E" $** > $@ |
| 1317 | |
| 1318 | "$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" |
| 1319 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c" |
| 1320 | |
| 1321 | "$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c" |
| 1322 | "$(OBJDIR)\translate$E" $** > $@ |
| 1323 | |
| @@ -1451,11 +1451,11 @@ | |
| 1451 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" |
| 1452 | |
| 1453 | "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" |
| 1454 | "$(OBJDIR)\translate$E" $** > $@ |
| 1455 | |
| 1456 | "$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" |
| 1457 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c" |
| 1458 | |
| 1459 | "$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c" |
| 1460 | "$(OBJDIR)\translate$E" $** > $@ |
| 1461 | |
| 1462 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -1313,11 +1313,11 @@ | |
| 1313 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" |
| 1314 | |
| 1315 | "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" |
| 1316 | "$(OBJDIR)\translate$E" $** > $@ |
| 1317 | |
| 1318 | "$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h" |
| 1319 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c" |
| 1320 | |
| 1321 | "$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c" |
| 1322 | "$(OBJDIR)\translate$E" $** > $@ |
| 1323 | |
| @@ -1451,11 +1451,11 @@ | |
| 1451 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" |
| 1452 | |
| 1453 | "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" |
| 1454 | "$(OBJDIR)\translate$E" $** > $@ |
| 1455 | |
| 1456 | "$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h" |
| 1457 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c" |
| 1458 | |
| 1459 | "$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c" |
| 1460 | "$(OBJDIR)\translate$E" $** > $@ |
| 1461 | |
| 1462 |
+25
-24
| --- www/aboutcgi.wiki | ||
| +++ www/aboutcgi.wiki | ||
| @@ -1,19 +1,20 @@ | ||
| 1 | 1 | <title>How CGI Works In Fossil</title> |
| 2 | 2 | <h2>Introduction</h2><blockquote> |
| 3 | -<p>CGI or "Common Gateway Interface" is a venerable yet reliable technique for | |
| 3 | +CGI or "Common Gateway Interface" is a venerable yet reliable technique for | |
| 4 | 4 | generating dynamic web content. This article gives a quick background on how |
| 5 | 5 | CGI works and describes how Fossil can act as a CGI service. |
| 6 | -<p>This is a "how it works" guide. This document provides background | |
| 6 | + | |
| 7 | +This is a "how it works" guide. This document provides background | |
| 7 | 8 | information on the CGI protocol so that you can better understand what |
| 8 | 9 | is going on behind the scenes. If you just want to set up Fossil |
| 9 | 10 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 10 | 11 | if you want to development CGI-based extensions to Fossil, see |
| 11 | 12 | the [./serverext.wiki|CGI Server Extensions] page. |
| 12 | 13 | </blockquote> |
| 13 | 14 | <h2>A Quick Review Of CGI</h2><blockquote> |
| 14 | -<p> | |
| 15 | + | |
| 15 | 16 | An HTTP request is a block of text that is sent by a client application |
| 16 | 17 | (usually a web browser) and arrives at the web server over a network |
| 17 | 18 | connection. The HTTP request contains a URL that describes the information |
| 18 | 19 | being requested. The URL in the HTTP request is typically the same URL |
| 19 | 20 | that appears in the URL bar at the top of the web browser that is making |
| @@ -20,33 +21,33 @@ | ||
| 20 | 21 | the request. The URL might contain a "?" character followed |
| 21 | 22 | query parameters. The HTTP will usually also contain other information |
| 22 | 23 | such as the name of the application that made the request, whether or |
| 23 | 24 | not the requesting application can accept a compressed reply, POST |
| 24 | 25 | parameters from forms, and so forth. |
| 25 | -<p> | |
| 26 | + | |
| 26 | 27 | The job of the web server is to interpret the HTTP request and formulate |
| 27 | 28 | an appropriate reply. |
| 28 | 29 | The web server is free to interpret the HTTP request in any way it wants. |
| 29 | 30 | But most web servers follow a similar pattern, described below. |
| 30 | 31 | (Note: details may vary from one web server to another.) |
| 31 | -<p> | |
| 32 | + | |
| 32 | 33 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 33 | 34 | <blockquote><b>/one/two/timeline/four</b></blockquote> |
| 34 | 35 | Most web servers will search their content area for files that match |
| 35 | 36 | some prefix of the URL. The search starts with <b>/one</b>, then goes to |
| 36 | 37 | <b>/one/two</b>, then <b>/one/two/timeline</b>, and finally |
| 37 | 38 | <b>/one/two/timeline/four</b> is checked. The search stops at the first |
| 38 | 39 | match. |
| 39 | -<p> | |
| 40 | + | |
| 40 | 41 | Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an |
| 41 | 42 | ordinary file in the content area, then that file is returned as static |
| 42 | 43 | content. The "<b>/timeline/four</b>" suffix is silently ignored. |
| 43 | -<p> | |
| 44 | + | |
| 44 | 45 | If <b>/one/two</b> is a CGI script (or program), then the web server |
| 45 | 46 | executes the <b>/one/two</b> script. The output generated by |
| 46 | 47 | the script is collected and repackaged as the HTTP reply. |
| 47 | -<p> | |
| 48 | + | |
| 48 | 49 | Before executing the CGI script, the web server will set up various |
| 49 | 50 | environment variables with information useful to the CGI script: |
| 50 | 51 | <table border=1 cellpadding=5> |
| 51 | 52 | <tr><th>Environment<br>Variable<th>Meaning |
| 52 | 53 | <tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0" |
| @@ -59,21 +60,21 @@ | ||
| 59 | 60 | <td>The suffix of the URL beyond the name of the CGI script. |
| 60 | 61 | In this example: "timeline/four". |
| 61 | 62 | <tr><td>QUERY_STRING |
| 62 | 63 | <td>The query string that follows the "?" in the URL, if there is one. |
| 63 | 64 | </table> |
| 64 | -<p> | |
| 65 | + | |
| 65 | 66 | There are other CGI environment variables beyond those listed above. |
| 66 | 67 | Many Fossil servers implement the |
| 67 | 68 | [https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env] |
| 68 | 69 | webpage that shows some of the CGI environment |
| 69 | 70 | variables that Fossil pays attention to. |
| 70 | -<p> | |
| 71 | + | |
| 71 | 72 | In addition to setting various CGI environment variables, if the HTTP |
| 72 | 73 | request contains POST content, then the web server relays the POST content |
| 73 | 74 | to standard input of the CGI script. |
| 74 | -<p> | |
| 75 | + | |
| 75 | 76 | In summary, the task of the |
| 76 | 77 | CGI script is to read the various CGI environment variables and |
| 77 | 78 | the POST content on standard input (if any), figure out an appropriate |
| 78 | 79 | reply, then write that reply on standard output. |
| 79 | 80 | The web server will read the output from the CGI script, reformat it |
| @@ -80,11 +81,11 @@ | ||
| 80 | 81 | into an appropriate HTTP reply, and relay the result back to the |
| 81 | 82 | requesting application. |
| 82 | 83 | The CGI script exits as soon as it generates a single reply. |
| 83 | 84 | The web server will (usually) persist and handle multiple HTTP requests, |
| 84 | 85 | but a CGI script handles just one HTTP request and then exits. |
| 85 | -<p> | |
| 86 | + | |
| 86 | 87 | The above is a rough outline of how CGI works. |
| 87 | 88 | There are many details omitted from this brief discussion. |
| 88 | 89 | See other on-line CGI tutorials for further information. |
| 89 | 90 | </blockquote> |
| 90 | 91 | <h2>How Fossil Acts As A CGI Program</h2> |
| @@ -103,33 +104,33 @@ | ||
| 103 | 104 | shebang with a single argument that is the full pathname of the script |
| 104 | 105 | itself. |
| 105 | 106 | In our example, the interpreter is Fossil, and the argument might |
| 106 | 107 | be something like "/var/www/cgi-bin/one/two" (depending on how your |
| 107 | 108 | particular web server is configured). |
| 108 | -<p> | |
| 109 | + | |
| 109 | 110 | The Fossil program that is run as the script interpreter |
| 110 | 111 | is the same Fossil that runs when |
| 111 | 112 | you type ordinary Fossil commands like "fossil sync" or "fossil commit". |
| 112 | 113 | But in this case, as soon as it launches, the Fossil program |
| 113 | 114 | recognizes that the GATEWAY_INTERFACE environment variable is |
| 114 | 115 | set to "CGI/1.0" and it therefore knows that it is being used as |
| 115 | 116 | CGI rather than as an ordinary command-line tool, and behaves accordingly. |
| 116 | -<p> | |
| 117 | + | |
| 117 | 118 | When Fossil recognizes that it is being run as CGI, it opens and reads |
| 118 | 119 | the file identified by its sole argument (the file named by |
| 119 | 120 | <code>argv[1]</code>). In our example, the second line of that file |
| 120 | 121 | tells Fossil the location of the repository it will be serving. |
| 121 | 122 | Fossil then starts looking at the CGI environment variables to figure |
| 122 | 123 | out what web page is being requested, generates that one web page, |
| 123 | 124 | then exits. |
| 124 | -<p> | |
| 125 | + | |
| 125 | 126 | Usually, the webpage being requested is the first term of the |
| 126 | 127 | PATH_INFO environment variable. (Exceptions to this rule are noted |
| 127 | 128 | in the sequel.) For our example, the first term of PATH_INFO |
| 128 | 129 | is "timeline", which means that Fossil will generate |
| 129 | 130 | the [/help?cmd=/timeline|/timeline] webpage. |
| 130 | -<p> | |
| 131 | + | |
| 131 | 132 | With Fossil, terms of PATH_INFO beyond the webpage name are converted into |
| 132 | 133 | the "name" query parameter. Hence, the following two URLs mean |
| 133 | 134 | exactly the same thing to Fossil: |
| 134 | 135 | <ol type='A'> |
| 135 | 136 | <li> [https://fossil-scm.org/home/info/c14ecc43] |
| @@ -149,11 +150,11 @@ | ||
| 149 | 150 | using a single CGI script. |
| 150 | 151 | On a website that wants to serve multiple repositories, one could |
| 151 | 152 | simply create multiple CGI scripts, one script for each repository. |
| 152 | 153 | But it is also possible to serve multiple Fossil repositories from |
| 153 | 154 | a single CGI script. |
| 154 | -<p> | |
| 155 | + | |
| 155 | 156 | If the CGI script for Fossil contains a "directory:" line instead of |
| 156 | 157 | a "repository:" line, then the argument to "directory:" is the name |
| 157 | 158 | of a directory that contains multiple repository files, each ending |
| 158 | 159 | with ".fossil". For example: |
| 159 | 160 | <blockquote><pre> |
| @@ -200,44 +201,44 @@ | ||
| 200 | 201 | HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING |
| 201 | 202 | </pre> |
| 202 | 203 | </blockquote> |
| 203 | 204 | <h2>Additional CGI Script Options</h2> |
| 204 | 205 | <blockquote> |
| 205 | -<p> | |
| 206 | + | |
| 206 | 207 | The CGI script can have additional options used to fine-tune |
| 207 | 208 | Fossil's behavior. See the [./cgi.wiki|CGI script documentation] |
| 208 | 209 | for details. |
| 209 | -</p> | |
| 210 | + | |
| 210 | 211 | </blockquote> |
| 211 | 212 | <h2>Additional Observations</h2> |
| 212 | 213 | <blockquote><ol type="I"> |
| 213 | 214 | <li><p> |
| 214 | 215 | Fossil does not distinguish between the various HTTP methods (GET, PUT, |
| 215 | 216 | DELETE, etc). Fossil figures out what it needs to do purely from the |
| 216 | -webpage term of the URI. | |
| 217 | +webpage term of the URI.</p></li> | |
| 217 | 218 | <li><p> |
| 218 | 219 | Fossil does not distinguish between query parameters that are part of the |
| 219 | 220 | URI, application/x-www-form-urlencoded or multipart/form-data encoded |
| 220 | 221 | parameter that are part of the POST content, and cookies. Each information |
| 221 | 222 | source is seen as a space of key/value pairs which are loaded into an |
| 222 | 223 | internal property hash table. The code that runs to generate the reply |
| 223 | 224 | can then reference various properties values. |
| 224 | 225 | Fossil does not care where the value of each property comes from (POST |
| 225 | 226 | content, cookies, or query parameters) only that the property exists |
| 226 | -and has a value. | |
| 227 | +and has a value.</p></li> | |
| 227 | 228 | <li><p> |
| 228 | 229 | The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands |
| 229 | 230 | are implemented using a simple built-in web server that accepts incoming HTTP |
| 230 | 231 | requests, translates each request into a CGI invocation, then creates a |
| 231 | 232 | separate child Fossil process to handle each request. In other words, CGI |
| 232 | 233 | is used internally to implement "fossil ui/server". |
| 233 | -<p> | |
| 234 | +<br><br> | |
| 234 | 235 | SCGI is processed using the same built-in web server, just modified |
| 235 | 236 | to parse SCGI requests instead of HTTP requests. Each SCGI request is |
| 236 | 237 | converted into CGI, then Fossil creates a separate child Fossil |
| 237 | -process to handle each CGI request. | |
| 238 | +process to handle each CGI request.</p></li> | |
| 238 | 239 | <li><p> |
| 239 | 240 | Fossil is itself often launched using CGI. But Fossil can also then |
| 240 | 241 | turn around and launch [./serverext.wiki|sub-CGI scripts to implement |
| 241 | -extensions]. | |
| 242 | +extensions].</p></li> | |
| 242 | 243 | </ol> |
| 243 | 244 | </blockquote> |
| 244 | 245 |
| --- www/aboutcgi.wiki | |
| +++ www/aboutcgi.wiki | |
| @@ -1,19 +1,20 @@ | |
| 1 | <title>How CGI Works In Fossil</title> |
| 2 | <h2>Introduction</h2><blockquote> |
| 3 | <p>CGI or "Common Gateway Interface" is a venerable yet reliable technique for |
| 4 | generating dynamic web content. This article gives a quick background on how |
| 5 | CGI works and describes how Fossil can act as a CGI service. |
| 6 | <p>This is a "how it works" guide. This document provides background |
| 7 | information on the CGI protocol so that you can better understand what |
| 8 | is going on behind the scenes. If you just want to set up Fossil |
| 9 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 10 | if you want to development CGI-based extensions to Fossil, see |
| 11 | the [./serverext.wiki|CGI Server Extensions] page. |
| 12 | </blockquote> |
| 13 | <h2>A Quick Review Of CGI</h2><blockquote> |
| 14 | <p> |
| 15 | An HTTP request is a block of text that is sent by a client application |
| 16 | (usually a web browser) and arrives at the web server over a network |
| 17 | connection. The HTTP request contains a URL that describes the information |
| 18 | being requested. The URL in the HTTP request is typically the same URL |
| 19 | that appears in the URL bar at the top of the web browser that is making |
| @@ -20,33 +21,33 @@ | |
| 20 | the request. The URL might contain a "?" character followed |
| 21 | query parameters. The HTTP will usually also contain other information |
| 22 | such as the name of the application that made the request, whether or |
| 23 | not the requesting application can accept a compressed reply, POST |
| 24 | parameters from forms, and so forth. |
| 25 | <p> |
| 26 | The job of the web server is to interpret the HTTP request and formulate |
| 27 | an appropriate reply. |
| 28 | The web server is free to interpret the HTTP request in any way it wants. |
| 29 | But most web servers follow a similar pattern, described below. |
| 30 | (Note: details may vary from one web server to another.) |
| 31 | <p> |
| 32 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 33 | <blockquote><b>/one/two/timeline/four</b></blockquote> |
| 34 | Most web servers will search their content area for files that match |
| 35 | some prefix of the URL. The search starts with <b>/one</b>, then goes to |
| 36 | <b>/one/two</b>, then <b>/one/two/timeline</b>, and finally |
| 37 | <b>/one/two/timeline/four</b> is checked. The search stops at the first |
| 38 | match. |
| 39 | <p> |
| 40 | Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an |
| 41 | ordinary file in the content area, then that file is returned as static |
| 42 | content. The "<b>/timeline/four</b>" suffix is silently ignored. |
| 43 | <p> |
| 44 | If <b>/one/two</b> is a CGI script (or program), then the web server |
| 45 | executes the <b>/one/two</b> script. The output generated by |
| 46 | the script is collected and repackaged as the HTTP reply. |
| 47 | <p> |
| 48 | Before executing the CGI script, the web server will set up various |
| 49 | environment variables with information useful to the CGI script: |
| 50 | <table border=1 cellpadding=5> |
| 51 | <tr><th>Environment<br>Variable<th>Meaning |
| 52 | <tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0" |
| @@ -59,21 +60,21 @@ | |
| 59 | <td>The suffix of the URL beyond the name of the CGI script. |
| 60 | In this example: "timeline/four". |
| 61 | <tr><td>QUERY_STRING |
| 62 | <td>The query string that follows the "?" in the URL, if there is one. |
| 63 | </table> |
| 64 | <p> |
| 65 | There are other CGI environment variables beyond those listed above. |
| 66 | Many Fossil servers implement the |
| 67 | [https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env] |
| 68 | webpage that shows some of the CGI environment |
| 69 | variables that Fossil pays attention to. |
| 70 | <p> |
| 71 | In addition to setting various CGI environment variables, if the HTTP |
| 72 | request contains POST content, then the web server relays the POST content |
| 73 | to standard input of the CGI script. |
| 74 | <p> |
| 75 | In summary, the task of the |
| 76 | CGI script is to read the various CGI environment variables and |
| 77 | the POST content on standard input (if any), figure out an appropriate |
| 78 | reply, then write that reply on standard output. |
| 79 | The web server will read the output from the CGI script, reformat it |
| @@ -80,11 +81,11 @@ | |
| 80 | into an appropriate HTTP reply, and relay the result back to the |
| 81 | requesting application. |
| 82 | The CGI script exits as soon as it generates a single reply. |
| 83 | The web server will (usually) persist and handle multiple HTTP requests, |
| 84 | but a CGI script handles just one HTTP request and then exits. |
| 85 | <p> |
| 86 | The above is a rough outline of how CGI works. |
| 87 | There are many details omitted from this brief discussion. |
| 88 | See other on-line CGI tutorials for further information. |
| 89 | </blockquote> |
| 90 | <h2>How Fossil Acts As A CGI Program</h2> |
| @@ -103,33 +104,33 @@ | |
| 103 | shebang with a single argument that is the full pathname of the script |
| 104 | itself. |
| 105 | In our example, the interpreter is Fossil, and the argument might |
| 106 | be something like "/var/www/cgi-bin/one/two" (depending on how your |
| 107 | particular web server is configured). |
| 108 | <p> |
| 109 | The Fossil program that is run as the script interpreter |
| 110 | is the same Fossil that runs when |
| 111 | you type ordinary Fossil commands like "fossil sync" or "fossil commit". |
| 112 | But in this case, as soon as it launches, the Fossil program |
| 113 | recognizes that the GATEWAY_INTERFACE environment variable is |
| 114 | set to "CGI/1.0" and it therefore knows that it is being used as |
| 115 | CGI rather than as an ordinary command-line tool, and behaves accordingly. |
| 116 | <p> |
| 117 | When Fossil recognizes that it is being run as CGI, it opens and reads |
| 118 | the file identified by its sole argument (the file named by |
| 119 | <code>argv[1]</code>). In our example, the second line of that file |
| 120 | tells Fossil the location of the repository it will be serving. |
| 121 | Fossil then starts looking at the CGI environment variables to figure |
| 122 | out what web page is being requested, generates that one web page, |
| 123 | then exits. |
| 124 | <p> |
| 125 | Usually, the webpage being requested is the first term of the |
| 126 | PATH_INFO environment variable. (Exceptions to this rule are noted |
| 127 | in the sequel.) For our example, the first term of PATH_INFO |
| 128 | is "timeline", which means that Fossil will generate |
| 129 | the [/help?cmd=/timeline|/timeline] webpage. |
| 130 | <p> |
| 131 | With Fossil, terms of PATH_INFO beyond the webpage name are converted into |
| 132 | the "name" query parameter. Hence, the following two URLs mean |
| 133 | exactly the same thing to Fossil: |
| 134 | <ol type='A'> |
| 135 | <li> [https://fossil-scm.org/home/info/c14ecc43] |
| @@ -149,11 +150,11 @@ | |
| 149 | using a single CGI script. |
| 150 | On a website that wants to serve multiple repositories, one could |
| 151 | simply create multiple CGI scripts, one script for each repository. |
| 152 | But it is also possible to serve multiple Fossil repositories from |
| 153 | a single CGI script. |
| 154 | <p> |
| 155 | If the CGI script for Fossil contains a "directory:" line instead of |
| 156 | a "repository:" line, then the argument to "directory:" is the name |
| 157 | of a directory that contains multiple repository files, each ending |
| 158 | with ".fossil". For example: |
| 159 | <blockquote><pre> |
| @@ -200,44 +201,44 @@ | |
| 200 | HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING |
| 201 | </pre> |
| 202 | </blockquote> |
| 203 | <h2>Additional CGI Script Options</h2> |
| 204 | <blockquote> |
| 205 | <p> |
| 206 | The CGI script can have additional options used to fine-tune |
| 207 | Fossil's behavior. See the [./cgi.wiki|CGI script documentation] |
| 208 | for details. |
| 209 | </p> |
| 210 | </blockquote> |
| 211 | <h2>Additional Observations</h2> |
| 212 | <blockquote><ol type="I"> |
| 213 | <li><p> |
| 214 | Fossil does not distinguish between the various HTTP methods (GET, PUT, |
| 215 | DELETE, etc). Fossil figures out what it needs to do purely from the |
| 216 | webpage term of the URI. |
| 217 | <li><p> |
| 218 | Fossil does not distinguish between query parameters that are part of the |
| 219 | URI, application/x-www-form-urlencoded or multipart/form-data encoded |
| 220 | parameter that are part of the POST content, and cookies. Each information |
| 221 | source is seen as a space of key/value pairs which are loaded into an |
| 222 | internal property hash table. The code that runs to generate the reply |
| 223 | can then reference various properties values. |
| 224 | Fossil does not care where the value of each property comes from (POST |
| 225 | content, cookies, or query parameters) only that the property exists |
| 226 | and has a value. |
| 227 | <li><p> |
| 228 | The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands |
| 229 | are implemented using a simple built-in web server that accepts incoming HTTP |
| 230 | requests, translates each request into a CGI invocation, then creates a |
| 231 | separate child Fossil process to handle each request. In other words, CGI |
| 232 | is used internally to implement "fossil ui/server". |
| 233 | <p> |
| 234 | SCGI is processed using the same built-in web server, just modified |
| 235 | to parse SCGI requests instead of HTTP requests. Each SCGI request is |
| 236 | converted into CGI, then Fossil creates a separate child Fossil |
| 237 | process to handle each CGI request. |
| 238 | <li><p> |
| 239 | Fossil is itself often launched using CGI. But Fossil can also then |
| 240 | turn around and launch [./serverext.wiki|sub-CGI scripts to implement |
| 241 | extensions]. |
| 242 | </ol> |
| 243 | </blockquote> |
| 244 |
| --- www/aboutcgi.wiki | |
| +++ www/aboutcgi.wiki | |
| @@ -1,19 +1,20 @@ | |
| 1 | <title>How CGI Works In Fossil</title> |
| 2 | <h2>Introduction</h2><blockquote> |
| 3 | CGI or "Common Gateway Interface" is a venerable yet reliable technique for |
| 4 | generating dynamic web content. This article gives a quick background on how |
| 5 | CGI works and describes how Fossil can act as a CGI service. |
| 6 | |
| 7 | This is a "how it works" guide. This document provides background |
| 8 | information on the CGI protocol so that you can better understand what |
| 9 | is going on behind the scenes. If you just want to set up Fossil |
| 10 | as a CGI server, see the [./server/ | Fossil Server Setup] page. Or |
| 11 | if you want to development CGI-based extensions to Fossil, see |
| 12 | the [./serverext.wiki|CGI Server Extensions] page. |
| 13 | </blockquote> |
| 14 | <h2>A Quick Review Of CGI</h2><blockquote> |
| 15 | |
| 16 | An HTTP request is a block of text that is sent by a client application |
| 17 | (usually a web browser) and arrives at the web server over a network |
| 18 | connection. The HTTP request contains a URL that describes the information |
| 19 | being requested. The URL in the HTTP request is typically the same URL |
| 20 | that appears in the URL bar at the top of the web browser that is making |
| @@ -20,33 +21,33 @@ | |
| 21 | the request. The URL might contain a "?" character followed |
| 22 | query parameters. The HTTP will usually also contain other information |
| 23 | such as the name of the application that made the request, whether or |
| 24 | not the requesting application can accept a compressed reply, POST |
| 25 | parameters from forms, and so forth. |
| 26 | |
| 27 | The job of the web server is to interpret the HTTP request and formulate |
| 28 | an appropriate reply. |
| 29 | The web server is free to interpret the HTTP request in any way it wants. |
| 30 | But most web servers follow a similar pattern, described below. |
| 31 | (Note: details may vary from one web server to another.) |
| 32 | |
| 33 | Suppose the filename component of the URL in the HTTP request looks like this: |
| 34 | <blockquote><b>/one/two/timeline/four</b></blockquote> |
| 35 | Most web servers will search their content area for files that match |
| 36 | some prefix of the URL. The search starts with <b>/one</b>, then goes to |
| 37 | <b>/one/two</b>, then <b>/one/two/timeline</b>, and finally |
| 38 | <b>/one/two/timeline/four</b> is checked. The search stops at the first |
| 39 | match. |
| 40 | |
| 41 | Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an |
| 42 | ordinary file in the content area, then that file is returned as static |
| 43 | content. The "<b>/timeline/four</b>" suffix is silently ignored. |
| 44 | |
| 45 | If <b>/one/two</b> is a CGI script (or program), then the web server |
| 46 | executes the <b>/one/two</b> script. The output generated by |
| 47 | the script is collected and repackaged as the HTTP reply. |
| 48 | |
| 49 | Before executing the CGI script, the web server will set up various |
| 50 | environment variables with information useful to the CGI script: |
| 51 | <table border=1 cellpadding=5> |
| 52 | <tr><th>Environment<br>Variable<th>Meaning |
| 53 | <tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0" |
| @@ -59,21 +60,21 @@ | |
| 60 | <td>The suffix of the URL beyond the name of the CGI script. |
| 61 | In this example: "timeline/four". |
| 62 | <tr><td>QUERY_STRING |
| 63 | <td>The query string that follows the "?" in the URL, if there is one. |
| 64 | </table> |
| 65 | |
| 66 | There are other CGI environment variables beyond those listed above. |
| 67 | Many Fossil servers implement the |
| 68 | [https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env] |
| 69 | webpage that shows some of the CGI environment |
| 70 | variables that Fossil pays attention to. |
| 71 | |
| 72 | In addition to setting various CGI environment variables, if the HTTP |
| 73 | request contains POST content, then the web server relays the POST content |
| 74 | to standard input of the CGI script. |
| 75 | |
| 76 | In summary, the task of the |
| 77 | CGI script is to read the various CGI environment variables and |
| 78 | the POST content on standard input (if any), figure out an appropriate |
| 79 | reply, then write that reply on standard output. |
| 80 | The web server will read the output from the CGI script, reformat it |
| @@ -80,11 +81,11 @@ | |
| 81 | into an appropriate HTTP reply, and relay the result back to the |
| 82 | requesting application. |
| 83 | The CGI script exits as soon as it generates a single reply. |
| 84 | The web server will (usually) persist and handle multiple HTTP requests, |
| 85 | but a CGI script handles just one HTTP request and then exits. |
| 86 | |
| 87 | The above is a rough outline of how CGI works. |
| 88 | There are many details omitted from this brief discussion. |
| 89 | See other on-line CGI tutorials for further information. |
| 90 | </blockquote> |
| 91 | <h2>How Fossil Acts As A CGI Program</h2> |
| @@ -103,33 +104,33 @@ | |
| 104 | shebang with a single argument that is the full pathname of the script |
| 105 | itself. |
| 106 | In our example, the interpreter is Fossil, and the argument might |
| 107 | be something like "/var/www/cgi-bin/one/two" (depending on how your |
| 108 | particular web server is configured). |
| 109 | |
| 110 | The Fossil program that is run as the script interpreter |
| 111 | is the same Fossil that runs when |
| 112 | you type ordinary Fossil commands like "fossil sync" or "fossil commit". |
| 113 | But in this case, as soon as it launches, the Fossil program |
| 114 | recognizes that the GATEWAY_INTERFACE environment variable is |
| 115 | set to "CGI/1.0" and it therefore knows that it is being used as |
| 116 | CGI rather than as an ordinary command-line tool, and behaves accordingly. |
| 117 | |
| 118 | When Fossil recognizes that it is being run as CGI, it opens and reads |
| 119 | the file identified by its sole argument (the file named by |
| 120 | <code>argv[1]</code>). In our example, the second line of that file |
| 121 | tells Fossil the location of the repository it will be serving. |
| 122 | Fossil then starts looking at the CGI environment variables to figure |
| 123 | out what web page is being requested, generates that one web page, |
| 124 | then exits. |
| 125 | |
| 126 | Usually, the webpage being requested is the first term of the |
| 127 | PATH_INFO environment variable. (Exceptions to this rule are noted |
| 128 | in the sequel.) For our example, the first term of PATH_INFO |
| 129 | is "timeline", which means that Fossil will generate |
| 130 | the [/help?cmd=/timeline|/timeline] webpage. |
| 131 | |
| 132 | With Fossil, terms of PATH_INFO beyond the webpage name are converted into |
| 133 | the "name" query parameter. Hence, the following two URLs mean |
| 134 | exactly the same thing to Fossil: |
| 135 | <ol type='A'> |
| 136 | <li> [https://fossil-scm.org/home/info/c14ecc43] |
| @@ -149,11 +150,11 @@ | |
| 150 | using a single CGI script. |
| 151 | On a website that wants to serve multiple repositories, one could |
| 152 | simply create multiple CGI scripts, one script for each repository. |
| 153 | But it is also possible to serve multiple Fossil repositories from |
| 154 | a single CGI script. |
| 155 | |
| 156 | If the CGI script for Fossil contains a "directory:" line instead of |
| 157 | a "repository:" line, then the argument to "directory:" is the name |
| 158 | of a directory that contains multiple repository files, each ending |
| 159 | with ".fossil". For example: |
| 160 | <blockquote><pre> |
| @@ -200,44 +201,44 @@ | |
| 201 | HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING |
| 202 | </pre> |
| 203 | </blockquote> |
| 204 | <h2>Additional CGI Script Options</h2> |
| 205 | <blockquote> |
| 206 | |
| 207 | The CGI script can have additional options used to fine-tune |
| 208 | Fossil's behavior. See the [./cgi.wiki|CGI script documentation] |
| 209 | for details. |
| 210 | |
| 211 | </blockquote> |
| 212 | <h2>Additional Observations</h2> |
| 213 | <blockquote><ol type="I"> |
| 214 | <li><p> |
| 215 | Fossil does not distinguish between the various HTTP methods (GET, PUT, |
| 216 | DELETE, etc). Fossil figures out what it needs to do purely from the |
| 217 | webpage term of the URI.</p></li> |
| 218 | <li><p> |
| 219 | Fossil does not distinguish between query parameters that are part of the |
| 220 | URI, application/x-www-form-urlencoded or multipart/form-data encoded |
| 221 | parameter that are part of the POST content, and cookies. Each information |
| 222 | source is seen as a space of key/value pairs which are loaded into an |
| 223 | internal property hash table. The code that runs to generate the reply |
| 224 | can then reference various properties values. |
| 225 | Fossil does not care where the value of each property comes from (POST |
| 226 | content, cookies, or query parameters) only that the property exists |
| 227 | and has a value.</p></li> |
| 228 | <li><p> |
| 229 | The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands |
| 230 | are implemented using a simple built-in web server that accepts incoming HTTP |
| 231 | requests, translates each request into a CGI invocation, then creates a |
| 232 | separate child Fossil process to handle each request. In other words, CGI |
| 233 | is used internally to implement "fossil ui/server". |
| 234 | <br><br> |
| 235 | SCGI is processed using the same built-in web server, just modified |
| 236 | to parse SCGI requests instead of HTTP requests. Each SCGI request is |
| 237 | converted into CGI, then Fossil creates a separate child Fossil |
| 238 | process to handle each CGI request.</p></li> |
| 239 | <li><p> |
| 240 | Fossil is itself often launched using CGI. But Fossil can also then |
| 241 | turn around and launch [./serverext.wiki|sub-CGI scripts to implement |
| 242 | extensions].</p></li> |
| 243 | </ol> |
| 244 | </blockquote> |
| 245 |
+1
-1
| --- www/adding_code.wiki | ||
| +++ www/adding_code.wiki | ||
| @@ -118,11 +118,11 @@ | ||
| 118 | 118 | |
| 119 | 119 | <blockquote><verbatim> |
| 120 | 120 | /* |
| 121 | 121 | ** COMMAND: xyzzy |
| 122 | 122 | ** |
| 123 | -** Help text goes here. | |
| 123 | +** Help text goes here. Backslashes must be escaped. | |
| 124 | 124 | */ |
| 125 | 125 | void xyzzy_cmd(void){ |
| 126 | 126 | /* Implement the command here */ |
| 127 | 127 | fossil_print("Hello, World!\n"); |
| 128 | 128 | } |
| 129 | 129 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -118,11 +118,11 @@ | |
| 118 | |
| 119 | <blockquote><verbatim> |
| 120 | /* |
| 121 | ** COMMAND: xyzzy |
| 122 | ** |
| 123 | ** Help text goes here. |
| 124 | */ |
| 125 | void xyzzy_cmd(void){ |
| 126 | /* Implement the command here */ |
| 127 | fossil_print("Hello, World!\n"); |
| 128 | } |
| 129 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -118,11 +118,11 @@ | |
| 118 | |
| 119 | <blockquote><verbatim> |
| 120 | /* |
| 121 | ** COMMAND: xyzzy |
| 122 | ** |
| 123 | ** Help text goes here. Backslashes must be escaped. |
| 124 | */ |
| 125 | void xyzzy_cmd(void){ |
| 126 | /* Implement the command here */ |
| 127 | fossil_print("Hello, World!\n"); |
| 128 | } |
| 129 |
+5
-4
| --- www/alerts.md | ||
| +++ www/alerts.md | ||
| @@ -366,15 +366,16 @@ | ||
| 366 | 366 | Statistically, this will happen about once every 70 or so messages, so |
| 367 | 367 | it is important to give this option if your MTA treats leading dots on a |
| 368 | 368 | line this way. |
| 369 | 369 | |
| 370 | 370 | <a id="msmtp"></a> |
| 371 | -We believe the [`msmtp`][msmtp] SMTP client is compatible with this | |
| 372 | -protocol if you give it the `-t` option. To our knowledge, this remains | |
| 373 | -untested, but if it works, this would be a useful option on a server | |
| 371 | +The [`msmtp`][msmtp] SMTP client is compatible with this | |
| 372 | +protocol if you give it the `-t` option. It’s a useful option on a server | |
| 374 | 373 | hosting a Fossil repository which doesn't otherwise require a separate |
| 375 | -SMTP server for other purposes. | |
| 374 | +SMTP server for other purposes, such as because you’ve got a separate | |
| 375 | +provider for your email and merely need a way to let Fossil feed | |
| 376 | +messages into it. | |
| 376 | 377 | |
| 377 | 378 | It is probably also possible to configure [`procmail`][pmdoc] to work |
| 378 | 379 | with this protocol. If you know how to do it, a patch to this document |
| 379 | 380 | or a how-to on [the Fossil forum][ff] would be appreciated. |
| 380 | 381 | |
| 381 | 382 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -366,15 +366,16 @@ | |
| 366 | Statistically, this will happen about once every 70 or so messages, so |
| 367 | it is important to give this option if your MTA treats leading dots on a |
| 368 | line this way. |
| 369 | |
| 370 | <a id="msmtp"></a> |
| 371 | We believe the [`msmtp`][msmtp] SMTP client is compatible with this |
| 372 | protocol if you give it the `-t` option. To our knowledge, this remains |
| 373 | untested, but if it works, this would be a useful option on a server |
| 374 | hosting a Fossil repository which doesn't otherwise require a separate |
| 375 | SMTP server for other purposes. |
| 376 | |
| 377 | It is probably also possible to configure [`procmail`][pmdoc] to work |
| 378 | with this protocol. If you know how to do it, a patch to this document |
| 379 | or a how-to on [the Fossil forum][ff] would be appreciated. |
| 380 | |
| 381 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -366,15 +366,16 @@ | |
| 366 | Statistically, this will happen about once every 70 or so messages, so |
| 367 | it is important to give this option if your MTA treats leading dots on a |
| 368 | line this way. |
| 369 | |
| 370 | <a id="msmtp"></a> |
| 371 | The [`msmtp`][msmtp] SMTP client is compatible with this |
| 372 | protocol if you give it the `-t` option. It’s a useful option on a server |
| 373 | hosting a Fossil repository which doesn't otherwise require a separate |
| 374 | SMTP server for other purposes, such as because you’ve got a separate |
| 375 | provider for your email and merely need a way to let Fossil feed |
| 376 | messages into it. |
| 377 | |
| 378 | It is probably also possible to configure [`procmail`][pmdoc] to work |
| 379 | with this protocol. If you know how to do it, a patch to this document |
| 380 | or a how-to on [the Fossil forum][ff] would be appreciated. |
| 381 | |
| 382 |
+1
-1
| --- www/blockchain.md | ||
| +++ www/blockchain.md | ||
| @@ -60,11 +60,11 @@ | ||
| 60 | 60 | * **Type 2** is creation of new fraudulent currency that will pass |
| 61 | 61 | in commerce. To extend our analogy, it is the creation of new |
| 62 | 62 | US $10 bills. There are two sub-types to this fraud. In terms of |
| 63 | 63 | our analogy, they are: |
| 64 | 64 | |
| 65 | - * **Type 2a**: copying an existing legitimate $10 bill<p> | |
| 65 | + * **Type 2a**: copying an existing legitimate $10 bill<br><br> | |
| 66 | 66 | |
| 67 | 67 | * **Type 2b**: printing a new $10 bill that is unlike an existing |
| 68 | 68 | legitimate one, yet which will still pass in commerce |
| 69 | 69 | |
| 70 | 70 | * **Type 3** is double-spending existing legitimate cryptocurrency. |
| 71 | 71 |
| --- www/blockchain.md | |
| +++ www/blockchain.md | |
| @@ -60,11 +60,11 @@ | |
| 60 | * **Type 2** is creation of new fraudulent currency that will pass |
| 61 | in commerce. To extend our analogy, it is the creation of new |
| 62 | US $10 bills. There are two sub-types to this fraud. In terms of |
| 63 | our analogy, they are: |
| 64 | |
| 65 | * **Type 2a**: copying an existing legitimate $10 bill<p> |
| 66 | |
| 67 | * **Type 2b**: printing a new $10 bill that is unlike an existing |
| 68 | legitimate one, yet which will still pass in commerce |
| 69 | |
| 70 | * **Type 3** is double-spending existing legitimate cryptocurrency. |
| 71 |
| --- www/blockchain.md | |
| +++ www/blockchain.md | |
| @@ -60,11 +60,11 @@ | |
| 60 | * **Type 2** is creation of new fraudulent currency that will pass |
| 61 | in commerce. To extend our analogy, it is the creation of new |
| 62 | US $10 bills. There are two sub-types to this fraud. In terms of |
| 63 | our analogy, they are: |
| 64 | |
| 65 | * **Type 2a**: copying an existing legitimate $10 bill<br><br> |
| 66 | |
| 67 | * **Type 2b**: printing a new $10 bill that is unlike an existing |
| 68 | legitimate one, yet which will still pass in commerce |
| 69 | |
| 70 | * **Type 3** is double-spending existing legitimate cryptocurrency. |
| 71 |
+56
-47
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -1,57 +1,57 @@ | ||
| 1 | 1 | <title>Compiling and Installing Fossil</title> |
| 2 | 2 | |
| 3 | 3 | <h2>0.0 Using A Pre-compiled Binary</h2> |
| 4 | 4 | |
| 5 | -<p>[/uv/download.html|Pre-compiled binaries] are available for recent | |
| 5 | +[/uv/download.html|Pre-compiled binaries] are available for recent | |
| 6 | 6 | releases. Just download |
| 7 | 7 | the appropriate executable for your platform |
| 8 | 8 | and put it on your $PATH. |
| 9 | 9 | To uninstall, simply delete the executable. |
| 10 | 10 | To upgrade from an older release, just overwrite the older binary with |
| 11 | -the newer one.</p> | |
| 11 | +the newer one. | |
| 12 | 12 | |
| 13 | -<p>For details about how those binaries are built, see | |
| 14 | -[/wiki?name=Release+Build+How-To | the Release Build How-To wiki page].</p> | |
| 13 | +For details about how those binaries are built, see | |
| 14 | +[/wiki?name=Release+Build+How-To | the Release Build How-To wiki page]. | |
| 15 | 15 | |
| 16 | 16 | |
| 17 | 17 | <h2>0.1 Executive Summary</h2> |
| 18 | 18 | |
| 19 | -<p>Building and installing is very simple. Three steps:</p> | |
| 19 | +Building and installing is very simple. Three steps: | |
| 20 | 20 | |
| 21 | 21 | <ol> |
| 22 | 22 | <li> Download and unpack a source tarball or ZIP. |
| 23 | 23 | <li> <b>./configure; make</b> |
| 24 | 24 | <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on |
| 25 | 25 | your $PATH. |
| 26 | 26 | </ol> |
| 27 | 27 | |
| 28 | -<p><hr> | |
| 28 | +<hr> | |
| 29 | 29 | |
| 30 | 30 | <h2>1.0 Obtaining The Source Code</h2> |
| 31 | 31 | |
| 32 | -<p>Fossil is self-hosting, so you can obtain a ZIP archive or tarball | |
| 32 | +Fossil is self-hosting, so you can obtain a ZIP archive or tarball | |
| 33 | 33 | containing a snapshot of the <em>latest</em> version directly from |
| 34 | 34 | Fossil's own fossil repository. Additionally, source archives of |
| 35 | 35 | <em>released</em> versions of |
| 36 | 36 | fossil are available from the [/uv/download.html|downloads page]. |
| 37 | -To obtain a development version of fossil, follow these steps:</p> | |
| 37 | +To obtain a development version of fossil, follow these steps: | |
| 38 | 38 | |
| 39 | 39 | <ol> |
| 40 | -<li><p>Point your web browser to [https://fossil-scm.org/]</li> | |
| 40 | +<li>Point your web browser to [https://fossil-scm.org/]</li> | |
| 41 | 41 | |
| 42 | -<li><p>Click on the [/timeline|Timeline] | |
| 43 | -link at the top of the page.</p></li> | |
| 42 | +<li>Click on the [/timeline|Timeline] | |
| 43 | +link at the top of the page.</li> | |
| 44 | 44 | |
| 45 | -<li><p>Select a version of of Fossil you want to download. The latest | |
| 45 | +<li>Select a version of of Fossil you want to download. The latest | |
| 46 | 46 | version on the trunk branch is usually a good choice. Click on its |
| 47 | -link.</p></li> | |
| 47 | +link.</li> | |
| 48 | 48 | |
| 49 | -<li><p>Finally, click on one of the | |
| 49 | +<li>Finally, click on one of the | |
| 50 | 50 | "Zip Archive" or "Tarball" links, according to your preference. |
| 51 | 51 | These link will build a ZIP archive or a gzip-compressed tarball of the |
| 52 | -complete source code and download it to your computer. | |
| 52 | +complete source code and download it to your computer.</li> | |
| 53 | 53 | </ol> |
| 54 | 54 | |
| 55 | 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | 56 | the Fossil source code?</h2> |
| 57 | 57 | |
| @@ -74,78 +74,82 @@ | ||
| 74 | 74 | |
| 75 | 75 | <h2>2.0 Compiling</h2> |
| 76 | 76 | |
| 77 | 77 | <ol> |
| 78 | 78 | <li value="5"> |
| 79 | -<p>Unpack the ZIP or tarball you downloaded then | |
| 80 | -<b>cd</b> into the directory created.</p></li> | |
| 79 | +Unpack the ZIP or tarball you downloaded then | |
| 80 | +<b>cd</b> into the directory created.</li> | |
| 81 | 81 | |
| 82 | 82 | <li><i>(Optional, Debian-compatible Linux only)</i> |
| 83 | 83 | Make sure you have all the necessary tools and libraries at hand by running: |
| 84 | 84 | <b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>. |
| 85 | 85 | |
| 86 | 86 | <li><i>(Optional, Unix only)</i> |
| 87 | 87 | Run <b>./configure</b> to construct a makefile. |
| 88 | 88 | |
| 89 | 89 | <ol type="a"> |
| 90 | -<li><p> | |
| 90 | +<li> | |
| 91 | 91 | The build system for Fossil on Unix-like systems assumes that the |
| 92 | 92 | OpenSSL development and runtime files are available on your system, |
| 93 | 93 | because unprotected repositories are trivial to attack otherwise. |
| 94 | 94 | Indeed, some public Fossil repositories — including Fossil's own — today |
| 95 | 95 | run in an HTTPS-only mode, so that you can't even do an anonymous clone |
| 96 | 96 | from them without using the TLS features added to Fossil by OpenSSL. To |
| 97 | 97 | weaken that stance could allow a |
| 98 | 98 | [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the |
| 99 | 99 | middle attack], such as one that substitutes malicious code into your |
| 100 | -Fossil repository clone.</p> | |
| 100 | +Fossil repository clone. | |
| 101 | 101 | |
| 102 | -<p>You can force the Fossil build system to avoid searching for, building | |
| 102 | +You can force the Fossil build system to avoid searching for, building | |
| 103 | 103 | against, and linking to the OpenSSL library by passing |
| 104 | -<b>--with-openssl=none</b> to the <tt>configure</tt> script.</p> | |
| 104 | +<b>--with-openssl=none</b> to the <tt>configure</tt> script. | |
| 105 | 105 | |
| 106 | -<p>If you do not have the OpenSSL development libraries on your system, | |
| 106 | +If you do not have the OpenSSL development libraries on your system, | |
| 107 | 107 | we recommend that you install them, typically via your OS's package |
| 108 | 108 | manager. The Fossil build system goes to a lot of effort to seek these |
| 109 | 109 | out wherever they may be found, so that is typically all you need to |
| 110 | -do.</p> | |
| 110 | +do. | |
| 111 | 111 | |
| 112 | -<p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL | |
| 113 | -discussion in the "TLS and Fossil" document].</p> | |
| 112 | +For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL | |
| 113 | +discussion in the "TLS and Fossil" document]. | |
| 114 | +</li> | |
| 114 | 115 | |
| 115 | -<li><p> | |
| 116 | +<li> | |
| 116 | 117 | To build a statically linked binary, you can <i>try</i> adding |
| 117 | 118 | the <b>--static</b> option, but |
| 118 | 119 | [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 119 | 120 | | it may well not work]. If your platform of choice is affected by this, |
| 120 | 121 | the simplest workaround we're aware of is to build a Fossil container, |
| 121 | 122 | then [./containers.md#static | extract the static executable from it]. |
| 123 | +</li> | |
| 122 | 124 | |
| 123 | -<li><p> | |
| 125 | +<li> | |
| 124 | 126 | To enable the native [./th1.md#tclEval | Tcl integration feature] feature, |
| 125 | 127 | add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options. |
| 128 | +</li> | |
| 126 | 129 | |
| 127 | -<li><p> | |
| 130 | +<li> | |
| 128 | 131 | Other configuration options can be seen by running |
| 129 | 132 | <b>./configure --help</b> |
| 133 | +</li> | |
| 130 | 134 | </ol> |
| 131 | 135 | |
| 132 | -<li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. | |
| 133 | -The details depend on your platform and compiler. | |
| 136 | +<li>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. | |
| 137 | +The details depend on your platform and compiler.</li> | |
| 134 | 138 | |
| 135 | 139 | <ol type="a"> |
| 136 | -<li><p><i>Unix</i> → the configure-generated Makefile should work on | |
| 137 | -all Unix and Unix-like systems. Simply type "<b>make</b>". | |
| 140 | +<li><i>Unix</i> → the configure-generated Makefile should work on | |
| 141 | +all Unix and Unix-like systems. Simply type "<b>make</b>".</li> | |
| 138 | 142 | |
| 139 | -<li><p><i>Unix without running "configure"</i> → if you prefer to avoid | |
| 143 | +<li><i>Unix without running "configure"</i> → if you prefer to avoid | |
| 140 | 144 | running configure, you can also use: <b>make -f Makefile.classic</b>. You may |
| 141 | 145 | want to make minor edits to Makefile.classic to configure the build for your |
| 142 | -system. | |
| 146 | +system.</li> | |
| 143 | 147 | |
| 144 | -<li><p><i>MinGW / MinGW-w64</i> → The best-supported path is to build | |
| 148 | +<li><i>MinGW / MinGW-w64</i> → The best-supported path is to build | |
| 145 | 149 | via the MinGW specific Makefile under a POSIX build of GNU make: |
| 146 | -"<b>make -f win/Makefile.mingw</b>". | |
| 150 | +"<b>make -f win/Makefile.mingw</b>".</li> | |
| 147 | 151 | |
| 148 | 152 | There is limited support for building under MinGW's native Windows port |
| 149 | 153 | of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but |
| 150 | 154 | it's better to build under MSYS, Cygwin, or WSL on Windows since this |
| 151 | 155 | mode doesn't take care of cases such as the "openssl" target, which |
| @@ -170,11 +174,11 @@ | ||
| 170 | 174 | Alternatively, running <b>./configure</b> under MSYS should give a |
| 171 | 175 | suitable top-level Makefile. However, options passed to configure that are |
| 172 | 176 | not applicable on Windows may cause the configuration or compilation to fail |
| 173 | 177 | (e.g. fusefs, internal-sqlite, etc). |
| 174 | 178 | |
| 175 | -<li><p><i>MSVC</i> → Use the MSVC makefile. | |
| 179 | +<li><i>MSVC</i> → Use the MSVC makefile.</li> | |
| 176 | 180 | |
| 177 | 181 | Run all of the following from a "x64 Native Tools Command Prompt". |
| 178 | 182 | |
| 179 | 183 | First |
| 180 | 184 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| @@ -202,58 +206,63 @@ | ||
| 202 | 206 | </pre></blockquote> |
| 203 | 207 | <blockquote><pre> |
| 204 | 208 | buildmsvc.bat FOSSIL_ENABLE_TCL=1 |
| 205 | 209 | </pre></blockquote> |
| 206 | 210 | |
| 207 | -<li><p><i>Cygwin</i> → The same as other Unix-like systems. It is | |
| 211 | +<li><i>Cygwin</i> → The same as other Unix-like systems. It is | |
| 208 | 212 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 209 | 213 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 210 | -"openssl-devel" packages installed first. | |
| 214 | +"openssl-devel" packages installed first.</li> | |
| 211 | 215 | </ol> |
| 212 | 216 | </ol> |
| 213 | 217 | |
| 214 | 218 | <h2>3.0 Installing</h2> |
| 215 | 219 | |
| 216 | 220 | <ol> |
| 217 | 221 | <li value="9"> |
| 218 | -<p>The finished binary is named "fossil" (or "fossil.exe" on Windows). | |
| 222 | +The finished binary is named "fossil" (or "fossil.exe" on Windows). | |
| 219 | 223 | Put this binary in a |
| 220 | 224 | directory that is somewhere on your PATH environment variable. |
| 221 | -It does not matter where.</p> | |
| 225 | +It does not matter where. | |
| 226 | +</li> | |
| 222 | 227 | |
| 223 | 228 | <li> |
| 224 | -<p><b>(Optional:)</b> | |
| 225 | -To uninstall, just delete the binary.</p> | |
| 229 | +<b>(Optional:)</b> | |
| 230 | +To uninstall, just delete the binary. | |
| 231 | +</li> | |
| 226 | 232 | </ol> |
| 227 | 233 | |
| 228 | 234 | <h2>4.0 Additional Considerations</h2> |
| 229 | 235 | |
| 230 | 236 | <ul> |
| 231 | -<li><p> | |
| 237 | +<li> | |
| 232 | 238 | If the makefiles that come with Fossil do not work for |
| 233 | 239 | you, or for some other reason you want to know how to build |
| 234 | 240 | Fossil manually, then refer to the |
| 235 | 241 | [./makefile.wiki | Fossil Build Process] document which describes |
| 236 | 242 | in detail what the makefiles do behind the scenes. |
| 243 | +</li> | |
| 237 | 244 | |
| 238 | -<li><p> | |
| 245 | +<li> | |
| 239 | 246 | The fossil executable is self-contained and stand-alone and usually |
| 240 | 247 | requires no special libraries or other software to be installed. However, |
| 241 | 248 | the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk |
| 242 | 249 | be installed on the local machine. You can get Tcl/Tk from |
| 243 | 250 | [http://www.activestate.com/activetcl|ActiveState]. |
| 251 | +</li> | |
| 244 | 252 | |
| 245 | -<li><p> | |
| 253 | +<li> | |
| 246 | 254 | To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile |
| 247 | 255 | generated by configure to add the following lines: |
| 248 | 256 | <blockquote><pre> |
| 249 | 257 | TCC += -DSQLITE_WITHOUT_ZONEMALLOC |
| 250 | 258 | TCC += -D_BSD_SOURCE |
| 251 | 259 | TCC += -DWITHOUT_ICONV |
| 252 | 260 | TCC += -Dsocketlen_t=int |
| 253 | 261 | TCC += -DSQLITE_MAX_MMAP_SIZE=0 |
| 254 | -</pre></blockquote> | |
| 262 | + </pre></blockquote> | |
| 263 | +</li> | |
| 255 | 264 | </ul> |
| 256 | 265 | |
| 257 | 266 | |
| 258 | 267 | <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> |
| 259 | 268 | |
| 260 | 269 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -1,57 +1,57 @@ | |
| 1 | <title>Compiling and Installing Fossil</title> |
| 2 | |
| 3 | <h2>0.0 Using A Pre-compiled Binary</h2> |
| 4 | |
| 5 | <p>[/uv/download.html|Pre-compiled binaries] are available for recent |
| 6 | releases. Just download |
| 7 | the appropriate executable for your platform |
| 8 | and put it on your $PATH. |
| 9 | To uninstall, simply delete the executable. |
| 10 | To upgrade from an older release, just overwrite the older binary with |
| 11 | the newer one.</p> |
| 12 | |
| 13 | <p>For details about how those binaries are built, see |
| 14 | [/wiki?name=Release+Build+How-To | the Release Build How-To wiki page].</p> |
| 15 | |
| 16 | |
| 17 | <h2>0.1 Executive Summary</h2> |
| 18 | |
| 19 | <p>Building and installing is very simple. Three steps:</p> |
| 20 | |
| 21 | <ol> |
| 22 | <li> Download and unpack a source tarball or ZIP. |
| 23 | <li> <b>./configure; make</b> |
| 24 | <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on |
| 25 | your $PATH. |
| 26 | </ol> |
| 27 | |
| 28 | <p><hr> |
| 29 | |
| 30 | <h2>1.0 Obtaining The Source Code</h2> |
| 31 | |
| 32 | <p>Fossil is self-hosting, so you can obtain a ZIP archive or tarball |
| 33 | containing a snapshot of the <em>latest</em> version directly from |
| 34 | Fossil's own fossil repository. Additionally, source archives of |
| 35 | <em>released</em> versions of |
| 36 | fossil are available from the [/uv/download.html|downloads page]. |
| 37 | To obtain a development version of fossil, follow these steps:</p> |
| 38 | |
| 39 | <ol> |
| 40 | <li><p>Point your web browser to [https://fossil-scm.org/]</li> |
| 41 | |
| 42 | <li><p>Click on the [/timeline|Timeline] |
| 43 | link at the top of the page.</p></li> |
| 44 | |
| 45 | <li><p>Select a version of of Fossil you want to download. The latest |
| 46 | version on the trunk branch is usually a good choice. Click on its |
| 47 | link.</p></li> |
| 48 | |
| 49 | <li><p>Finally, click on one of the |
| 50 | "Zip Archive" or "Tarball" links, according to your preference. |
| 51 | These link will build a ZIP archive or a gzip-compressed tarball of the |
| 52 | complete source code and download it to your computer. |
| 53 | </ol> |
| 54 | |
| 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | the Fossil source code?</h2> |
| 57 | |
| @@ -74,78 +74,82 @@ | |
| 74 | |
| 75 | <h2>2.0 Compiling</h2> |
| 76 | |
| 77 | <ol> |
| 78 | <li value="5"> |
| 79 | <p>Unpack the ZIP or tarball you downloaded then |
| 80 | <b>cd</b> into the directory created.</p></li> |
| 81 | |
| 82 | <li><i>(Optional, Debian-compatible Linux only)</i> |
| 83 | Make sure you have all the necessary tools and libraries at hand by running: |
| 84 | <b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>. |
| 85 | |
| 86 | <li><i>(Optional, Unix only)</i> |
| 87 | Run <b>./configure</b> to construct a makefile. |
| 88 | |
| 89 | <ol type="a"> |
| 90 | <li><p> |
| 91 | The build system for Fossil on Unix-like systems assumes that the |
| 92 | OpenSSL development and runtime files are available on your system, |
| 93 | because unprotected repositories are trivial to attack otherwise. |
| 94 | Indeed, some public Fossil repositories — including Fossil's own — today |
| 95 | run in an HTTPS-only mode, so that you can't even do an anonymous clone |
| 96 | from them without using the TLS features added to Fossil by OpenSSL. To |
| 97 | weaken that stance could allow a |
| 98 | [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the |
| 99 | middle attack], such as one that substitutes malicious code into your |
| 100 | Fossil repository clone.</p> |
| 101 | |
| 102 | <p>You can force the Fossil build system to avoid searching for, building |
| 103 | against, and linking to the OpenSSL library by passing |
| 104 | <b>--with-openssl=none</b> to the <tt>configure</tt> script.</p> |
| 105 | |
| 106 | <p>If you do not have the OpenSSL development libraries on your system, |
| 107 | we recommend that you install them, typically via your OS's package |
| 108 | manager. The Fossil build system goes to a lot of effort to seek these |
| 109 | out wherever they may be found, so that is typically all you need to |
| 110 | do.</p> |
| 111 | |
| 112 | <p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL |
| 113 | discussion in the "TLS and Fossil" document].</p> |
| 114 | |
| 115 | <li><p> |
| 116 | To build a statically linked binary, you can <i>try</i> adding |
| 117 | the <b>--static</b> option, but |
| 118 | [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 119 | | it may well not work]. If your platform of choice is affected by this, |
| 120 | the simplest workaround we're aware of is to build a Fossil container, |
| 121 | then [./containers.md#static | extract the static executable from it]. |
| 122 | |
| 123 | <li><p> |
| 124 | To enable the native [./th1.md#tclEval | Tcl integration feature] feature, |
| 125 | add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options. |
| 126 | |
| 127 | <li><p> |
| 128 | Other configuration options can be seen by running |
| 129 | <b>./configure --help</b> |
| 130 | </ol> |
| 131 | |
| 132 | <li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. |
| 133 | The details depend on your platform and compiler. |
| 134 | |
| 135 | <ol type="a"> |
| 136 | <li><p><i>Unix</i> → the configure-generated Makefile should work on |
| 137 | all Unix and Unix-like systems. Simply type "<b>make</b>". |
| 138 | |
| 139 | <li><p><i>Unix without running "configure"</i> → if you prefer to avoid |
| 140 | running configure, you can also use: <b>make -f Makefile.classic</b>. You may |
| 141 | want to make minor edits to Makefile.classic to configure the build for your |
| 142 | system. |
| 143 | |
| 144 | <li><p><i>MinGW / MinGW-w64</i> → The best-supported path is to build |
| 145 | via the MinGW specific Makefile under a POSIX build of GNU make: |
| 146 | "<b>make -f win/Makefile.mingw</b>". |
| 147 | |
| 148 | There is limited support for building under MinGW's native Windows port |
| 149 | of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but |
| 150 | it's better to build under MSYS, Cygwin, or WSL on Windows since this |
| 151 | mode doesn't take care of cases such as the "openssl" target, which |
| @@ -170,11 +174,11 @@ | |
| 170 | Alternatively, running <b>./configure</b> under MSYS should give a |
| 171 | suitable top-level Makefile. However, options passed to configure that are |
| 172 | not applicable on Windows may cause the configuration or compilation to fail |
| 173 | (e.g. fusefs, internal-sqlite, etc). |
| 174 | |
| 175 | <li><p><i>MSVC</i> → Use the MSVC makefile. |
| 176 | |
| 177 | Run all of the following from a "x64 Native Tools Command Prompt". |
| 178 | |
| 179 | First |
| 180 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| @@ -202,58 +206,63 @@ | |
| 202 | </pre></blockquote> |
| 203 | <blockquote><pre> |
| 204 | buildmsvc.bat FOSSIL_ENABLE_TCL=1 |
| 205 | </pre></blockquote> |
| 206 | |
| 207 | <li><p><i>Cygwin</i> → The same as other Unix-like systems. It is |
| 208 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 209 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 210 | "openssl-devel" packages installed first. |
| 211 | </ol> |
| 212 | </ol> |
| 213 | |
| 214 | <h2>3.0 Installing</h2> |
| 215 | |
| 216 | <ol> |
| 217 | <li value="9"> |
| 218 | <p>The finished binary is named "fossil" (or "fossil.exe" on Windows). |
| 219 | Put this binary in a |
| 220 | directory that is somewhere on your PATH environment variable. |
| 221 | It does not matter where.</p> |
| 222 | |
| 223 | <li> |
| 224 | <p><b>(Optional:)</b> |
| 225 | To uninstall, just delete the binary.</p> |
| 226 | </ol> |
| 227 | |
| 228 | <h2>4.0 Additional Considerations</h2> |
| 229 | |
| 230 | <ul> |
| 231 | <li><p> |
| 232 | If the makefiles that come with Fossil do not work for |
| 233 | you, or for some other reason you want to know how to build |
| 234 | Fossil manually, then refer to the |
| 235 | [./makefile.wiki | Fossil Build Process] document which describes |
| 236 | in detail what the makefiles do behind the scenes. |
| 237 | |
| 238 | <li><p> |
| 239 | The fossil executable is self-contained and stand-alone and usually |
| 240 | requires no special libraries or other software to be installed. However, |
| 241 | the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk |
| 242 | be installed on the local machine. You can get Tcl/Tk from |
| 243 | [http://www.activestate.com/activetcl|ActiveState]. |
| 244 | |
| 245 | <li><p> |
| 246 | To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile |
| 247 | generated by configure to add the following lines: |
| 248 | <blockquote><pre> |
| 249 | TCC += -DSQLITE_WITHOUT_ZONEMALLOC |
| 250 | TCC += -D_BSD_SOURCE |
| 251 | TCC += -DWITHOUT_ICONV |
| 252 | TCC += -Dsocketlen_t=int |
| 253 | TCC += -DSQLITE_MAX_MMAP_SIZE=0 |
| 254 | </pre></blockquote> |
| 255 | </ul> |
| 256 | |
| 257 | |
| 258 | <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> |
| 259 | |
| 260 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -1,57 +1,57 @@ | |
| 1 | <title>Compiling and Installing Fossil</title> |
| 2 | |
| 3 | <h2>0.0 Using A Pre-compiled Binary</h2> |
| 4 | |
| 5 | [/uv/download.html|Pre-compiled binaries] are available for recent |
| 6 | releases. Just download |
| 7 | the appropriate executable for your platform |
| 8 | and put it on your $PATH. |
| 9 | To uninstall, simply delete the executable. |
| 10 | To upgrade from an older release, just overwrite the older binary with |
| 11 | the newer one. |
| 12 | |
| 13 | For details about how those binaries are built, see |
| 14 | [/wiki?name=Release+Build+How-To | the Release Build How-To wiki page]. |
| 15 | |
| 16 | |
| 17 | <h2>0.1 Executive Summary</h2> |
| 18 | |
| 19 | Building and installing is very simple. Three steps: |
| 20 | |
| 21 | <ol> |
| 22 | <li> Download and unpack a source tarball or ZIP. |
| 23 | <li> <b>./configure; make</b> |
| 24 | <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on |
| 25 | your $PATH. |
| 26 | </ol> |
| 27 | |
| 28 | <hr> |
| 29 | |
| 30 | <h2>1.0 Obtaining The Source Code</h2> |
| 31 | |
| 32 | Fossil is self-hosting, so you can obtain a ZIP archive or tarball |
| 33 | containing a snapshot of the <em>latest</em> version directly from |
| 34 | Fossil's own fossil repository. Additionally, source archives of |
| 35 | <em>released</em> versions of |
| 36 | fossil are available from the [/uv/download.html|downloads page]. |
| 37 | To obtain a development version of fossil, follow these steps: |
| 38 | |
| 39 | <ol> |
| 40 | <li>Point your web browser to [https://fossil-scm.org/]</li> |
| 41 | |
| 42 | <li>Click on the [/timeline|Timeline] |
| 43 | link at the top of the page.</li> |
| 44 | |
| 45 | <li>Select a version of of Fossil you want to download. The latest |
| 46 | version on the trunk branch is usually a good choice. Click on its |
| 47 | link.</li> |
| 48 | |
| 49 | <li>Finally, click on one of the |
| 50 | "Zip Archive" or "Tarball" links, according to your preference. |
| 51 | These link will build a ZIP archive or a gzip-compressed tarball of the |
| 52 | complete source code and download it to your computer.</li> |
| 53 | </ol> |
| 54 | |
| 55 | <h2>Aside: Is it really safe to use an unreleased development version of |
| 56 | the Fossil source code?</h2> |
| 57 | |
| @@ -74,78 +74,82 @@ | |
| 74 | |
| 75 | <h2>2.0 Compiling</h2> |
| 76 | |
| 77 | <ol> |
| 78 | <li value="5"> |
| 79 | Unpack the ZIP or tarball you downloaded then |
| 80 | <b>cd</b> into the directory created.</li> |
| 81 | |
| 82 | <li><i>(Optional, Debian-compatible Linux only)</i> |
| 83 | Make sure you have all the necessary tools and libraries at hand by running: |
| 84 | <b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>. |
| 85 | |
| 86 | <li><i>(Optional, Unix only)</i> |
| 87 | Run <b>./configure</b> to construct a makefile. |
| 88 | |
| 89 | <ol type="a"> |
| 90 | <li> |
| 91 | The build system for Fossil on Unix-like systems assumes that the |
| 92 | OpenSSL development and runtime files are available on your system, |
| 93 | because unprotected repositories are trivial to attack otherwise. |
| 94 | Indeed, some public Fossil repositories — including Fossil's own — today |
| 95 | run in an HTTPS-only mode, so that you can't even do an anonymous clone |
| 96 | from them without using the TLS features added to Fossil by OpenSSL. To |
| 97 | weaken that stance could allow a |
| 98 | [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the |
| 99 | middle attack], such as one that substitutes malicious code into your |
| 100 | Fossil repository clone. |
| 101 | |
| 102 | You can force the Fossil build system to avoid searching for, building |
| 103 | against, and linking to the OpenSSL library by passing |
| 104 | <b>--with-openssl=none</b> to the <tt>configure</tt> script. |
| 105 | |
| 106 | If you do not have the OpenSSL development libraries on your system, |
| 107 | we recommend that you install them, typically via your OS's package |
| 108 | manager. The Fossil build system goes to a lot of effort to seek these |
| 109 | out wherever they may be found, so that is typically all you need to |
| 110 | do. |
| 111 | |
| 112 | For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL |
| 113 | discussion in the "TLS and Fossil" document]. |
| 114 | </li> |
| 115 | |
| 116 | <li> |
| 117 | To build a statically linked binary, you can <i>try</i> adding |
| 118 | the <b>--static</b> option, but |
| 119 | [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 120 | | it may well not work]. If your platform of choice is affected by this, |
| 121 | the simplest workaround we're aware of is to build a Fossil container, |
| 122 | then [./containers.md#static | extract the static executable from it]. |
| 123 | </li> |
| 124 | |
| 125 | <li> |
| 126 | To enable the native [./th1.md#tclEval | Tcl integration feature] feature, |
| 127 | add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options. |
| 128 | </li> |
| 129 | |
| 130 | <li> |
| 131 | Other configuration options can be seen by running |
| 132 | <b>./configure --help</b> |
| 133 | </li> |
| 134 | </ol> |
| 135 | |
| 136 | <li>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. |
| 137 | The details depend on your platform and compiler.</li> |
| 138 | |
| 139 | <ol type="a"> |
| 140 | <li><i>Unix</i> → the configure-generated Makefile should work on |
| 141 | all Unix and Unix-like systems. Simply type "<b>make</b>".</li> |
| 142 | |
| 143 | <li><i>Unix without running "configure"</i> → if you prefer to avoid |
| 144 | running configure, you can also use: <b>make -f Makefile.classic</b>. You may |
| 145 | want to make minor edits to Makefile.classic to configure the build for your |
| 146 | system.</li> |
| 147 | |
| 148 | <li><i>MinGW / MinGW-w64</i> → The best-supported path is to build |
| 149 | via the MinGW specific Makefile under a POSIX build of GNU make: |
| 150 | "<b>make -f win/Makefile.mingw</b>".</li> |
| 151 | |
| 152 | There is limited support for building under MinGW's native Windows port |
| 153 | of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but |
| 154 | it's better to build under MSYS, Cygwin, or WSL on Windows since this |
| 155 | mode doesn't take care of cases such as the "openssl" target, which |
| @@ -170,11 +174,11 @@ | |
| 174 | Alternatively, running <b>./configure</b> under MSYS should give a |
| 175 | suitable top-level Makefile. However, options passed to configure that are |
| 176 | not applicable on Windows may cause the configuration or compilation to fail |
| 177 | (e.g. fusefs, internal-sqlite, etc). |
| 178 | |
| 179 | <li><i>MSVC</i> → Use the MSVC makefile.</li> |
| 180 | |
| 181 | Run all of the following from a "x64 Native Tools Command Prompt". |
| 182 | |
| 183 | First |
| 184 | change to the "win/" subdirectory ("<b>cd win</b>") then run |
| @@ -202,58 +206,63 @@ | |
| 206 | </pre></blockquote> |
| 207 | <blockquote><pre> |
| 208 | buildmsvc.bat FOSSIL_ENABLE_TCL=1 |
| 209 | </pre></blockquote> |
| 210 | |
| 211 | <li><i>Cygwin</i> → The same as other Unix-like systems. It is |
| 212 | recommended to configure using: "<b>configure --disable-internal-sqlite</b>", |
| 213 | making sure you have the "libsqlite3-devel" , "zlib-devel" and |
| 214 | "openssl-devel" packages installed first.</li> |
| 215 | </ol> |
| 216 | </ol> |
| 217 | |
| 218 | <h2>3.0 Installing</h2> |
| 219 | |
| 220 | <ol> |
| 221 | <li value="9"> |
| 222 | The finished binary is named "fossil" (or "fossil.exe" on Windows). |
| 223 | Put this binary in a |
| 224 | directory that is somewhere on your PATH environment variable. |
| 225 | It does not matter where. |
| 226 | </li> |
| 227 | |
| 228 | <li> |
| 229 | <b>(Optional:)</b> |
| 230 | To uninstall, just delete the binary. |
| 231 | </li> |
| 232 | </ol> |
| 233 | |
| 234 | <h2>4.0 Additional Considerations</h2> |
| 235 | |
| 236 | <ul> |
| 237 | <li> |
| 238 | If the makefiles that come with Fossil do not work for |
| 239 | you, or for some other reason you want to know how to build |
| 240 | Fossil manually, then refer to the |
| 241 | [./makefile.wiki | Fossil Build Process] document which describes |
| 242 | in detail what the makefiles do behind the scenes. |
| 243 | </li> |
| 244 | |
| 245 | <li> |
| 246 | The fossil executable is self-contained and stand-alone and usually |
| 247 | requires no special libraries or other software to be installed. However, |
| 248 | the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk |
| 249 | be installed on the local machine. You can get Tcl/Tk from |
| 250 | [http://www.activestate.com/activetcl|ActiveState]. |
| 251 | </li> |
| 252 | |
| 253 | <li> |
| 254 | To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile |
| 255 | generated by configure to add the following lines: |
| 256 | <blockquote><pre> |
| 257 | TCC += -DSQLITE_WITHOUT_ZONEMALLOC |
| 258 | TCC += -D_BSD_SOURCE |
| 259 | TCC += -DWITHOUT_ICONV |
| 260 | TCC += -Dsocketlen_t=int |
| 261 | TCC += -DSQLITE_MAX_MMAP_SIZE=0 |
| 262 | </pre></blockquote> |
| 263 | </li> |
| 264 | </ul> |
| 265 | |
| 266 | |
| 267 | <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> |
| 268 | |
| 269 |
+52
-17
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,13 +1,48 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | -<h2 id='v2_22'>Changes for version 2.22 (pending)</h2> | |
| 3 | +<h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> | |
| 4 | + * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> | |
| 5 | + <li> Add the ft=TAG query parameter which in combination with d=Y | |
| 6 | + shows all descendants of Y up to TAG | |
| 7 | + <li> Enhance the s=PATTERN (search) query parameter so that forum post | |
| 8 | + text is also searched when the "vfx" query parameter is used | |
| 9 | + <li> Fix the u= (user) query parameter so that it works with a= and b= | |
| 10 | + <li> Add the oldestfirst query parameter to show the events in reverse order. | |
| 11 | + Useful in combination with y=f and vfs and perhaps also u= to show all | |
| 12 | + forum events in chronological order | |
| 13 | + <li> For the p=X and bt=Y query parameter combination, if Y is a tag that | |
| 14 | + identifies multiple check-ins, search backwards in time for Y beginning | |
| 15 | + at X | |
| 16 | + </ol> | |
| 17 | + * Administrators can select to skip sending notifications about new forum | |
| 18 | + posts. | |
| 19 | + * If the value N is negative in "--context N" or "-c N" to the various diff | |
| 20 | + commands, then treat it as infinite and show the complete file content. | |
| 4 | 21 | * The stock OCI container no longer includes BusyBox, thus no longer |
| 5 | 22 | needs to start as root to chroot that power away. That in turn |
| 6 | 23 | frees us from needing to build and install the container as root, |
| 7 | 24 | since it no longer has to create a private <tt>/dev</tt> tree |
| 8 | 25 | inside the jail for Fossil's use. |
| 26 | + * Add support for the trigram tokenizer for FTS5 search to enable | |
| 27 | + searching in Chinese. | |
| 28 | + * Comment lines (starting with a '#') are now supported inside | |
| 29 | + [./settings.wiki#versionable|versioned settings]. | |
| 30 | + * Default permissions for anonymous users in new repositories is | |
| 31 | + changed to "hz". | |
| 32 | + * The [/help?cmd=status|fossil status] command now detects when a | |
| 33 | + file used to be a symlink and has been replaced by a regular file. | |
| 34 | + (It previously checked for the inverse case only.) | |
| 35 | + * The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same | |
| 36 | + parser as the *-glob settings instead of its prior idiosyncratic | |
| 37 | + parser, allowing quoted whitespace in patterns. | |
| 38 | + * Enhancements to the [/help?cmd=/reports|/reports webpage]: | |
| 39 | + <ol type="a"> | |
| 40 | + <li> The by-week, by-month, and by-year options now show an estimated | |
| 41 | + size of the current week, month, or year as a dashed box. | |
| 42 | + <li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins". | |
| 43 | + </ol> | |
| 9 | 44 | |
| 10 | 45 | <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2> |
| 11 | 46 | * Users can request a password reset. This feature is disabledby default. Use |
| 12 | 47 | the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it. |
| 13 | 48 | New web pages [/help?cmd=/resetpw|/resetpw] and |
| @@ -64,17 +99,17 @@ | ||
| 64 | 99 | numeric order even if they contain a different number of digits. |
| 65 | 100 | (Example: "fossil_80_..." comes before "fossil_100.png" in the |
| 66 | 101 | [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.) |
| 67 | 102 | * Enhancements to the graph layout algorithm design to improve readability |
| 68 | 103 | and promote better situational awareness. |
| 69 | - * Performance enhancement for the | |
| 104 | + * Performance enhancement for the | |
| 70 | 105 | [./checkin_names.wiki#root|"root:BRANCHNAME" style of tag], |
| 71 | 106 | accomplished using a Common Table Expression in the underlying SQL. |
| 72 | 107 | * Sort tag listings (command line and webpage) by taking numbers into |
| 73 | 108 | consideration so as to cater for tags that follow semantic versioning. |
| 74 | 109 | * On the wiki listings, omit by default wiki pages that are associated with |
| 75 | - check-ins and branches. | |
| 110 | + check-ins and branches. | |
| 76 | 111 | * Add the new "[/help?cmd=describe|fossil describe]" command. |
| 77 | 112 | * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support]. |
| 78 | 113 | See corresponding [../test/markdown-test3.md|test cases], |
| 79 | 114 | [/wiki?name=branch/markdown-footnotes#il|known limitations] and |
| 80 | 115 | [forum:/forumthread/ee1f1597e46ec07a|discussion]. |
| @@ -123,11 +158,11 @@ | ||
| 123 | 158 | </ul> |
| 124 | 159 | * Promote the test-detach command into the [/help?cmd=detach|detach command]. |
| 125 | 160 | * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option, |
| 126 | 161 | if no URL is specified then use the last URL from the most recent prior |
| 127 | 162 | "fossil pull --from-parent-project". |
| 128 | - * Add options --project-name and --project-desc to the | |
| 163 | + * Add options --project-name and --project-desc to the | |
| 129 | 164 | "[/help?cmd=init|fossil init]" command. |
| 130 | 165 | * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment |
| 131 | 166 | variable for clients. |
| 132 | 167 | * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such |
| 133 | 168 | that it includes the query string. This is how most other systems understand |
| @@ -145,20 +180,20 @@ | ||
| 145 | 180 | <li> Performance improvements |
| 146 | 181 | </ul> |
| 147 | 182 | * The --branchcolor option on [/help?cmd=commit|fossil commit] and |
| 148 | 183 | [/help?cmd=amend|fossil amend] can now take the value "auto" to |
| 149 | 184 | force Fossil to use its built-in automatic color choosing algorithm. |
| 150 | - * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running | |
| 185 | + * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running | |
| 151 | 186 | [/help?cmd=open|fossil open]. |
| 152 | 187 | * Add the [/help?cmd=ticket-default-report|ticket-default-report setting], |
| 153 | 188 | which if set to the title of a ticket report causes that ticket report |
| 154 | 189 | to be displayed below the search box in the /ticket page. |
| 155 | 190 | * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page |
| 156 | 191 | causes all graph coloring to be omitted. |
| 157 | 192 | * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]" |
| 158 | 193 | feature so that it works better on a wider variety of platforms. |
| 159 | - * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for | |
| 194 | + * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for | |
| 160 | 195 | the current page and list URLs suitable for pasting them into the page. |
| 161 | 196 | * Add the --no-http-compression option to [/help?cmd=sync|fossil sync] |
| 162 | 197 | and similar. |
| 163 | 198 | * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using |
| 164 | 199 | the --verbose option. |
| @@ -249,11 +284,11 @@ | ||
| 249 | 284 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 250 | 285 | images to be loaded from any URL. All other resources are still |
| 251 | 286 | locked down by default. |
| 252 | 287 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 253 | 288 | setting to determine the content of the main menu. |
| 254 | - The ability to edit the | |
| 289 | + The ability to edit the | |
| 255 | 290 | "mainmenu" setting is added on the /Admin/Configuration page. |
| 256 | 291 | * The hamburger menu is now available on most of the built-in skins. |
| 257 | 292 | * Any built-in skin named "X" can be used instead of the standard |
| 258 | 293 | repository skin by adding the URL parameter <tt>skin=X</tt> to the |
| 259 | 294 | request. The selection is persisted using the display |
| @@ -264,43 +299,43 @@ | ||
| 264 | 299 | /sitemap, so that it appears in hamburger menus. |
| 265 | 300 | * The [/sitemap] extensions are now specified by a single new |
| 266 | 301 | "[/help?cmd=sitemap-extra|sitemap-extra setting]", |
| 267 | 302 | rather than a cluster of various |
| 268 | 303 | "sitemap-*" settings. The older settings are no longer used. |
| 269 | - <b>This change might require minor server configuration | |
| 304 | + <b>This change might require minor server configuration | |
| 270 | 305 | adjustments on servers that use /sitemap extensions.</b> |
| 271 | 306 | The /Admin/Configuration page provides the ability to edit |
| 272 | 307 | the new "sitemap-extra" setting. |
| 273 | - * Added the "--ckout-alias NAME" option to | |
| 308 | + * Added the "--ckout-alias NAME" option to | |
| 274 | 309 | [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and |
| 275 | 310 | [/help?cmd=http|fossil http]. This option causes Fossil to |
| 276 | 311 | understand URIs of the form "/doc/NAME/..." as if they were |
| 277 | 312 | "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of |
| 278 | 313 | [./embeddeddoc.wiki|embedded documentation] changes prior to |
| 279 | 314 | check-in. |
| 280 | 315 | * For diff web pages, if the diff type (unified versus side-by-side) |
| 281 | - is not specified by a query parameter, and if the | |
| 316 | + is not specified by a query parameter, and if the | |
| 282 | 317 | "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 283 | 318 | setting is omitted or less than 1, then select the diff type based |
| 284 | 319 | on a guess of whether or not the request is coming from a mobile |
| 285 | 320 | device. Mobile gets unified and desktop gets side-by-side. |
| 286 | 321 | * The various pages which show diffs now have toggles to show/hide |
| 287 | 322 | individual diffs. |
| 288 | 323 | * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 289 | 324 | setting to allow an admin to force a default diff type. |
| 290 | - * The "pikchr-background" settings is now available in | |
| 325 | + * The "pikchr-background" settings is now available in | |
| 291 | 326 | "detail.txt" skin files, for better control of Pikchr |
| 292 | 327 | colors in inverted color schemes. |
| 293 | - * Add the <tt>--list</tt> option to the | |
| 328 | + * Add the <tt>--list</tt> option to the | |
| 294 | 329 | [/help?cmd=tarball|tarball], |
| 295 | 330 | [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] |
| 296 | 331 | commands. |
| 297 | 332 | * The javascript used to implement the hamburger menu on the |
| 298 | 333 | default built-in skin has been made generic so that it is usable |
| 299 | 334 | by a variety of skins, and promoted to an ordinary built-in |
| 300 | 335 | javascript file. |
| 301 | - * New TH1 commands: | |
| 336 | + * New TH1 commands: | |
| 302 | 337 | "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]", |
| 303 | 338 | "[/doc/trunk/www/th1.md#capexpr|capexpr]", |
| 304 | 339 | "foreach", "lappend", and "string match" |
| 305 | 340 | * The [/help/leaves|leaves command] now shows the branch point |
| 306 | 341 | of each leaf. |
| @@ -333,11 +368,11 @@ | ||
| 333 | 368 | * <b>Schema Update Notice #1:</b> |
| 334 | 369 | This release drops a trigger from the database schema (replacing |
| 335 | 370 | it with a TEMP trigger that is created as needed). This |
| 336 | 371 | change happens automatically the first time you |
| 337 | 372 | add content to a repository using Fossil 2.14 or later. No |
| 338 | - action is needed on your part. However, if you upgrade to | |
| 373 | + action is needed on your part. However, if you upgrade to | |
| 339 | 374 | version 2.14 and then later downgrade or otherwise use an earlier |
| 340 | 375 | version of Fossil, the email notification mechanism may fail |
| 341 | 376 | to send out notifications for some events, due to the missing |
| 342 | 377 | trigger. If you want to |
| 343 | 378 | permanently downgrade an installation, then you should run |
| @@ -358,11 +393,11 @@ | ||
| 358 | 393 | from the remote URL and the newly cloned repo is opened. This makes |
| 359 | 394 | the clone command work more like Git, thus making it easier for |
| 360 | 395 | people transitioning from Git. |
| 361 | 396 | * Added the --mainbranch option to the [/help?cmd=git|fossil git export] |
| 362 | 397 | command. |
| 363 | - * Added the --format option to the | |
| 398 | + * Added the --format option to the | |
| 364 | 399 | "[/help?cmd=timeline|fossil timeline]" command. |
| 365 | 400 | * Enhance the --numstat option on the |
| 366 | 401 | "[/help?cmd=diff|fossil diff]" command so that it shows a total |
| 367 | 402 | number of lines added and deleted and total number of files |
| 368 | 403 | modified. |
| @@ -480,11 +515,11 @@ | ||
| 480 | 515 | of the checkin that occured on 2020-06-27 15:06 going back to |
| 481 | 516 | the 2.11 release. |
| 482 | 517 | * Update the built-in SQLite so that the |
| 483 | 518 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 484 | 519 | modes ".mode box" and ".mode json". |
| 485 | - * Add the "<tt>obscure()</tt>" SQL function to the | |
| 520 | + * Add the "<tt>obscure()</tt>" SQL function to the | |
| 486 | 521 | "[/help?cmd=sql|fossil sql]" command. |
| 487 | 522 | * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to |
| 488 | 523 | the "[/help?cmd=sql|fossil sql]" command, providing access to the |
| 489 | 524 | dispatch table including all help text, and the builtin data files, |
| 490 | 525 | respectively. |
| @@ -517,11 +552,11 @@ | ||
| 517 | 552 | to work without the rebuild, but the new backlinks will be missing.</ul> |
| 518 | 553 | * The algorithm for finding the |
| 519 | 554 | [./tech_overview.wiki#configloc|location of the configuration database] |
| 520 | 555 | is enhanced to be XDG-compliant. |
| 521 | 556 | * Add a hide/show feature to |
| 522 | - [./wikitheory.wiki#assocwiki|associated wiki] display on | |
| 557 | + [./wikitheory.wiki#assocwiki|associated wiki] display on | |
| 523 | 558 | check-in and branch information pages. |
| 524 | 559 | * Enhance the "[/help?cmd=info|fossil info]" command so that it |
| 525 | 560 | works with no arguments even if not within an open check-out. |
| 526 | 561 | * Many improvements to the forum and especially email notification |
| 527 | 562 | of forum posts, in response to community feedback after switching |
| 528 | 563 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,13 +1,48 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_22'>Changes for version 2.22 (pending)</h2> |
| 4 | * The stock OCI container no longer includes BusyBox, thus no longer |
| 5 | needs to start as root to chroot that power away. That in turn |
| 6 | frees us from needing to build and install the container as root, |
| 7 | since it no longer has to create a private <tt>/dev</tt> tree |
| 8 | inside the jail for Fossil's use. |
| 9 | |
| 10 | <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2> |
| 11 | * Users can request a password reset. This feature is disabledby default. Use |
| 12 | the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it. |
| 13 | New web pages [/help?cmd=/resetpw|/resetpw] and |
| @@ -64,17 +99,17 @@ | |
| 64 | numeric order even if they contain a different number of digits. |
| 65 | (Example: "fossil_80_..." comes before "fossil_100.png" in the |
| 66 | [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.) |
| 67 | * Enhancements to the graph layout algorithm design to improve readability |
| 68 | and promote better situational awareness. |
| 69 | * Performance enhancement for the |
| 70 | [./checkin_names.wiki#root|"root:BRANCHNAME" style of tag], |
| 71 | accomplished using a Common Table Expression in the underlying SQL. |
| 72 | * Sort tag listings (command line and webpage) by taking numbers into |
| 73 | consideration so as to cater for tags that follow semantic versioning. |
| 74 | * On the wiki listings, omit by default wiki pages that are associated with |
| 75 | check-ins and branches. |
| 76 | * Add the new "[/help?cmd=describe|fossil describe]" command. |
| 77 | * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support]. |
| 78 | See corresponding [../test/markdown-test3.md|test cases], |
| 79 | [/wiki?name=branch/markdown-footnotes#il|known limitations] and |
| 80 | [forum:/forumthread/ee1f1597e46ec07a|discussion]. |
| @@ -123,11 +158,11 @@ | |
| 123 | </ul> |
| 124 | * Promote the test-detach command into the [/help?cmd=detach|detach command]. |
| 125 | * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option, |
| 126 | if no URL is specified then use the last URL from the most recent prior |
| 127 | "fossil pull --from-parent-project". |
| 128 | * Add options --project-name and --project-desc to the |
| 129 | "[/help?cmd=init|fossil init]" command. |
| 130 | * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment |
| 131 | variable for clients. |
| 132 | * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such |
| 133 | that it includes the query string. This is how most other systems understand |
| @@ -145,20 +180,20 @@ | |
| 145 | <li> Performance improvements |
| 146 | </ul> |
| 147 | * The --branchcolor option on [/help?cmd=commit|fossil commit] and |
| 148 | [/help?cmd=amend|fossil amend] can now take the value "auto" to |
| 149 | force Fossil to use its built-in automatic color choosing algorithm. |
| 150 | * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running |
| 151 | [/help?cmd=open|fossil open]. |
| 152 | * Add the [/help?cmd=ticket-default-report|ticket-default-report setting], |
| 153 | which if set to the title of a ticket report causes that ticket report |
| 154 | to be displayed below the search box in the /ticket page. |
| 155 | * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page |
| 156 | causes all graph coloring to be omitted. |
| 157 | * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]" |
| 158 | feature so that it works better on a wider variety of platforms. |
| 159 | * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for |
| 160 | the current page and list URLs suitable for pasting them into the page. |
| 161 | * Add the --no-http-compression option to [/help?cmd=sync|fossil sync] |
| 162 | and similar. |
| 163 | * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using |
| 164 | the --verbose option. |
| @@ -249,11 +284,11 @@ | |
| 249 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 250 | images to be loaded from any URL. All other resources are still |
| 251 | locked down by default. |
| 252 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 253 | setting to determine the content of the main menu. |
| 254 | The ability to edit the |
| 255 | "mainmenu" setting is added on the /Admin/Configuration page. |
| 256 | * The hamburger menu is now available on most of the built-in skins. |
| 257 | * Any built-in skin named "X" can be used instead of the standard |
| 258 | repository skin by adding the URL parameter <tt>skin=X</tt> to the |
| 259 | request. The selection is persisted using the display |
| @@ -264,43 +299,43 @@ | |
| 264 | /sitemap, so that it appears in hamburger menus. |
| 265 | * The [/sitemap] extensions are now specified by a single new |
| 266 | "[/help?cmd=sitemap-extra|sitemap-extra setting]", |
| 267 | rather than a cluster of various |
| 268 | "sitemap-*" settings. The older settings are no longer used. |
| 269 | <b>This change might require minor server configuration |
| 270 | adjustments on servers that use /sitemap extensions.</b> |
| 271 | The /Admin/Configuration page provides the ability to edit |
| 272 | the new "sitemap-extra" setting. |
| 273 | * Added the "--ckout-alias NAME" option to |
| 274 | [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and |
| 275 | [/help?cmd=http|fossil http]. This option causes Fossil to |
| 276 | understand URIs of the form "/doc/NAME/..." as if they were |
| 277 | "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of |
| 278 | [./embeddeddoc.wiki|embedded documentation] changes prior to |
| 279 | check-in. |
| 280 | * For diff web pages, if the diff type (unified versus side-by-side) |
| 281 | is not specified by a query parameter, and if the |
| 282 | "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 283 | setting is omitted or less than 1, then select the diff type based |
| 284 | on a guess of whether or not the request is coming from a mobile |
| 285 | device. Mobile gets unified and desktop gets side-by-side. |
| 286 | * The various pages which show diffs now have toggles to show/hide |
| 287 | individual diffs. |
| 288 | * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 289 | setting to allow an admin to force a default diff type. |
| 290 | * The "pikchr-background" settings is now available in |
| 291 | "detail.txt" skin files, for better control of Pikchr |
| 292 | colors in inverted color schemes. |
| 293 | * Add the <tt>--list</tt> option to the |
| 294 | [/help?cmd=tarball|tarball], |
| 295 | [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] |
| 296 | commands. |
| 297 | * The javascript used to implement the hamburger menu on the |
| 298 | default built-in skin has been made generic so that it is usable |
| 299 | by a variety of skins, and promoted to an ordinary built-in |
| 300 | javascript file. |
| 301 | * New TH1 commands: |
| 302 | "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]", |
| 303 | "[/doc/trunk/www/th1.md#capexpr|capexpr]", |
| 304 | "foreach", "lappend", and "string match" |
| 305 | * The [/help/leaves|leaves command] now shows the branch point |
| 306 | of each leaf. |
| @@ -333,11 +368,11 @@ | |
| 333 | * <b>Schema Update Notice #1:</b> |
| 334 | This release drops a trigger from the database schema (replacing |
| 335 | it with a TEMP trigger that is created as needed). This |
| 336 | change happens automatically the first time you |
| 337 | add content to a repository using Fossil 2.14 or later. No |
| 338 | action is needed on your part. However, if you upgrade to |
| 339 | version 2.14 and then later downgrade or otherwise use an earlier |
| 340 | version of Fossil, the email notification mechanism may fail |
| 341 | to send out notifications for some events, due to the missing |
| 342 | trigger. If you want to |
| 343 | permanently downgrade an installation, then you should run |
| @@ -358,11 +393,11 @@ | |
| 358 | from the remote URL and the newly cloned repo is opened. This makes |
| 359 | the clone command work more like Git, thus making it easier for |
| 360 | people transitioning from Git. |
| 361 | * Added the --mainbranch option to the [/help?cmd=git|fossil git export] |
| 362 | command. |
| 363 | * Added the --format option to the |
| 364 | "[/help?cmd=timeline|fossil timeline]" command. |
| 365 | * Enhance the --numstat option on the |
| 366 | "[/help?cmd=diff|fossil diff]" command so that it shows a total |
| 367 | number of lines added and deleted and total number of files |
| 368 | modified. |
| @@ -480,11 +515,11 @@ | |
| 480 | of the checkin that occured on 2020-06-27 15:06 going back to |
| 481 | the 2.11 release. |
| 482 | * Update the built-in SQLite so that the |
| 483 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 484 | modes ".mode box" and ".mode json". |
| 485 | * Add the "<tt>obscure()</tt>" SQL function to the |
| 486 | "[/help?cmd=sql|fossil sql]" command. |
| 487 | * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to |
| 488 | the "[/help?cmd=sql|fossil sql]" command, providing access to the |
| 489 | dispatch table including all help text, and the builtin data files, |
| 490 | respectively. |
| @@ -517,11 +552,11 @@ | |
| 517 | to work without the rebuild, but the new backlinks will be missing.</ul> |
| 518 | * The algorithm for finding the |
| 519 | [./tech_overview.wiki#configloc|location of the configuration database] |
| 520 | is enhanced to be XDG-compliant. |
| 521 | * Add a hide/show feature to |
| 522 | [./wikitheory.wiki#assocwiki|associated wiki] display on |
| 523 | check-in and branch information pages. |
| 524 | * Enhance the "[/help?cmd=info|fossil info]" command so that it |
| 525 | works with no arguments even if not within an open check-out. |
| 526 | * Many improvements to the forum and especially email notification |
| 527 | of forum posts, in response to community feedback after switching |
| 528 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,13 +1,48 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2> |
| 4 | * Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a"> |
| 5 | <li> Add the ft=TAG query parameter which in combination with d=Y |
| 6 | shows all descendants of Y up to TAG |
| 7 | <li> Enhance the s=PATTERN (search) query parameter so that forum post |
| 8 | text is also searched when the "vfx" query parameter is used |
| 9 | <li> Fix the u= (user) query parameter so that it works with a= and b= |
| 10 | <li> Add the oldestfirst query parameter to show the events in reverse order. |
| 11 | Useful in combination with y=f and vfs and perhaps also u= to show all |
| 12 | forum events in chronological order |
| 13 | <li> For the p=X and bt=Y query parameter combination, if Y is a tag that |
| 14 | identifies multiple check-ins, search backwards in time for Y beginning |
| 15 | at X |
| 16 | </ol> |
| 17 | * Administrators can select to skip sending notifications about new forum |
| 18 | posts. |
| 19 | * If the value N is negative in "--context N" or "-c N" to the various diff |
| 20 | commands, then treat it as infinite and show the complete file content. |
| 21 | * The stock OCI container no longer includes BusyBox, thus no longer |
| 22 | needs to start as root to chroot that power away. That in turn |
| 23 | frees us from needing to build and install the container as root, |
| 24 | since it no longer has to create a private <tt>/dev</tt> tree |
| 25 | inside the jail for Fossil's use. |
| 26 | * Add support for the trigram tokenizer for FTS5 search to enable |
| 27 | searching in Chinese. |
| 28 | * Comment lines (starting with a '#') are now supported inside |
| 29 | [./settings.wiki#versionable|versioned settings]. |
| 30 | * Default permissions for anonymous users in new repositories is |
| 31 | changed to "hz". |
| 32 | * The [/help?cmd=status|fossil status] command now detects when a |
| 33 | file used to be a symlink and has been replaced by a regular file. |
| 34 | (It previously checked for the inverse case only.) |
| 35 | * The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same |
| 36 | parser as the *-glob settings instead of its prior idiosyncratic |
| 37 | parser, allowing quoted whitespace in patterns. |
| 38 | * Enhancements to the [/help?cmd=/reports|/reports webpage]: |
| 39 | <ol type="a"> |
| 40 | <li> The by-week, by-month, and by-year options now show an estimated |
| 41 | size of the current week, month, or year as a dashed box. |
| 42 | <li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins". |
| 43 | </ol> |
| 44 | |
| 45 | <h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2> |
| 46 | * Users can request a password reset. This feature is disabledby default. Use |
| 47 | the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it. |
| 48 | New web pages [/help?cmd=/resetpw|/resetpw] and |
| @@ -64,17 +99,17 @@ | |
| 99 | numeric order even if they contain a different number of digits. |
| 100 | (Example: "fossil_80_..." comes before "fossil_100.png" in the |
| 101 | [/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.) |
| 102 | * Enhancements to the graph layout algorithm design to improve readability |
| 103 | and promote better situational awareness. |
| 104 | * Performance enhancement for the |
| 105 | [./checkin_names.wiki#root|"root:BRANCHNAME" style of tag], |
| 106 | accomplished using a Common Table Expression in the underlying SQL. |
| 107 | * Sort tag listings (command line and webpage) by taking numbers into |
| 108 | consideration so as to cater for tags that follow semantic versioning. |
| 109 | * On the wiki listings, omit by default wiki pages that are associated with |
| 110 | check-ins and branches. |
| 111 | * Add the new "[/help?cmd=describe|fossil describe]" command. |
| 112 | * Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support]. |
| 113 | See corresponding [../test/markdown-test3.md|test cases], |
| 114 | [/wiki?name=branch/markdown-footnotes#il|known limitations] and |
| 115 | [forum:/forumthread/ee1f1597e46ec07a|discussion]. |
| @@ -123,11 +158,11 @@ | |
| 158 | </ul> |
| 159 | * Promote the test-detach command into the [/help?cmd=detach|detach command]. |
| 160 | * For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option, |
| 161 | if no URL is specified then use the last URL from the most recent prior |
| 162 | "fossil pull --from-parent-project". |
| 163 | * Add options --project-name and --project-desc to the |
| 164 | "[/help?cmd=init|fossil init]" command. |
| 165 | * The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment |
| 166 | variable for clients. |
| 167 | * Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such |
| 168 | that it includes the query string. This is how most other systems understand |
| @@ -145,20 +180,20 @@ | |
| 180 | <li> Performance improvements |
| 181 | </ul> |
| 182 | * The --branchcolor option on [/help?cmd=commit|fossil commit] and |
| 183 | [/help?cmd=amend|fossil amend] can now take the value "auto" to |
| 184 | force Fossil to use its built-in automatic color choosing algorithm. |
| 185 | * Fossil now [./concepts.wiki#workflow|autosyncs] prior to running |
| 186 | [/help?cmd=open|fossil open]. |
| 187 | * Add the [/help?cmd=ticket-default-report|ticket-default-report setting], |
| 188 | which if set to the title of a ticket report causes that ticket report |
| 189 | to be displayed below the search box in the /ticket page. |
| 190 | * The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page |
| 191 | causes all graph coloring to be omitted. |
| 192 | * Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]" |
| 193 | feature so that it works better on a wider variety of platforms. |
| 194 | * In [/help?cmd=/wikiedit|/wikiedit], show the list of attachments for |
| 195 | the current page and list URLs suitable for pasting them into the page. |
| 196 | * Add the --no-http-compression option to [/help?cmd=sync|fossil sync] |
| 197 | and similar. |
| 198 | * Print total payload bytes on a [/help?cmd=sync|fossil sync] when using |
| 199 | the --verbose option. |
| @@ -249,11 +284,11 @@ | |
| 284 | * The [./defcsp.md|default CSP] has been relaxed slightly to allow |
| 285 | images to be loaded from any URL. All other resources are still |
| 286 | locked down by default. |
| 287 | * The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]" |
| 288 | setting to determine the content of the main menu. |
| 289 | The ability to edit the |
| 290 | "mainmenu" setting is added on the /Admin/Configuration page. |
| 291 | * The hamburger menu is now available on most of the built-in skins. |
| 292 | * Any built-in skin named "X" can be used instead of the standard |
| 293 | repository skin by adding the URL parameter <tt>skin=X</tt> to the |
| 294 | request. The selection is persisted using the display |
| @@ -264,43 +299,43 @@ | |
| 299 | /sitemap, so that it appears in hamburger menus. |
| 300 | * The [/sitemap] extensions are now specified by a single new |
| 301 | "[/help?cmd=sitemap-extra|sitemap-extra setting]", |
| 302 | rather than a cluster of various |
| 303 | "sitemap-*" settings. The older settings are no longer used. |
| 304 | <b>This change might require minor server configuration |
| 305 | adjustments on servers that use /sitemap extensions.</b> |
| 306 | The /Admin/Configuration page provides the ability to edit |
| 307 | the new "sitemap-extra" setting. |
| 308 | * Added the "--ckout-alias NAME" option to |
| 309 | [/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and |
| 310 | [/help?cmd=http|fossil http]. This option causes Fossil to |
| 311 | understand URIs of the form "/doc/NAME/..." as if they were |
| 312 | "[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of |
| 313 | [./embeddeddoc.wiki|embedded documentation] changes prior to |
| 314 | check-in. |
| 315 | * For diff web pages, if the diff type (unified versus side-by-side) |
| 316 | is not specified by a query parameter, and if the |
| 317 | "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 318 | setting is omitted or less than 1, then select the diff type based |
| 319 | on a guess of whether or not the request is coming from a mobile |
| 320 | device. Mobile gets unified and desktop gets side-by-side. |
| 321 | * The various pages which show diffs now have toggles to show/hide |
| 322 | individual diffs. |
| 323 | * Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]" |
| 324 | setting to allow an admin to force a default diff type. |
| 325 | * The "pikchr-background" settings is now available in |
| 326 | "detail.txt" skin files, for better control of Pikchr |
| 327 | colors in inverted color schemes. |
| 328 | * Add the <tt>--list</tt> option to the |
| 329 | [/help?cmd=tarball|tarball], |
| 330 | [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] |
| 331 | commands. |
| 332 | * The javascript used to implement the hamburger menu on the |
| 333 | default built-in skin has been made generic so that it is usable |
| 334 | by a variety of skins, and promoted to an ordinary built-in |
| 335 | javascript file. |
| 336 | * New TH1 commands: |
| 337 | "[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]", |
| 338 | "[/doc/trunk/www/th1.md#capexpr|capexpr]", |
| 339 | "foreach", "lappend", and "string match" |
| 340 | * The [/help/leaves|leaves command] now shows the branch point |
| 341 | of each leaf. |
| @@ -333,11 +368,11 @@ | |
| 368 | * <b>Schema Update Notice #1:</b> |
| 369 | This release drops a trigger from the database schema (replacing |
| 370 | it with a TEMP trigger that is created as needed). This |
| 371 | change happens automatically the first time you |
| 372 | add content to a repository using Fossil 2.14 or later. No |
| 373 | action is needed on your part. However, if you upgrade to |
| 374 | version 2.14 and then later downgrade or otherwise use an earlier |
| 375 | version of Fossil, the email notification mechanism may fail |
| 376 | to send out notifications for some events, due to the missing |
| 377 | trigger. If you want to |
| 378 | permanently downgrade an installation, then you should run |
| @@ -358,11 +393,11 @@ | |
| 393 | from the remote URL and the newly cloned repo is opened. This makes |
| 394 | the clone command work more like Git, thus making it easier for |
| 395 | people transitioning from Git. |
| 396 | * Added the --mainbranch option to the [/help?cmd=git|fossil git export] |
| 397 | command. |
| 398 | * Added the --format option to the |
| 399 | "[/help?cmd=timeline|fossil timeline]" command. |
| 400 | * Enhance the --numstat option on the |
| 401 | "[/help?cmd=diff|fossil diff]" command so that it shows a total |
| 402 | number of lines added and deleted and total number of files |
| 403 | modified. |
| @@ -480,11 +515,11 @@ | |
| 515 | of the checkin that occured on 2020-06-27 15:06 going back to |
| 516 | the 2.11 release. |
| 517 | * Update the built-in SQLite so that the |
| 518 | "[/help?cmd=sql|fossil sql]" command supports new output |
| 519 | modes ".mode box" and ".mode json". |
| 520 | * Add the "<tt>obscure()</tt>" SQL function to the |
| 521 | "[/help?cmd=sql|fossil sql]" command. |
| 522 | * Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to |
| 523 | the "[/help?cmd=sql|fossil sql]" command, providing access to the |
| 524 | dispatch table including all help text, and the builtin data files, |
| 525 | respectively. |
| @@ -517,11 +552,11 @@ | |
| 552 | to work without the rebuild, but the new backlinks will be missing.</ul> |
| 553 | * The algorithm for finding the |
| 554 | [./tech_overview.wiki#configloc|location of the configuration database] |
| 555 | is enhanced to be XDG-compliant. |
| 556 | * Add a hide/show feature to |
| 557 | [./wikitheory.wiki#assocwiki|associated wiki] display on |
| 558 | check-in and branch information pages. |
| 559 | * Enhance the "[/help?cmd=info|fossil info]" command so that it |
| 560 | works with no arguments even if not within an open check-out. |
| 561 | * Many improvements to the forum and especially email notification |
| 562 | of forum posts, in response to community feedback after switching |
| 563 |
+22
-24
| --- www/concepts.wiki | ||
| +++ www/concepts.wiki | ||
| @@ -159,33 +159,33 @@ | ||
| 159 | 159 | of the manifest is the identifier for the entire check-in. When |
| 160 | 160 | you look at a "timeline" of changes in Fossil, the ID associated |
| 161 | 161 | with each check-in or commit is really just the artifact ID of the |
| 162 | 162 | manifest for that check-in. |
| 163 | 163 | |
| 164 | -<p>The manifest file is not normally a real file on disk. Instead, | |
| 164 | +The manifest file is not normally a real file on disk. Instead, | |
| 165 | 165 | the manifest is computed in memory by Fossil whenever it needs it. |
| 166 | 166 | However, the "fossil setting manifest on" command will cause the |
| 167 | 167 | manifest file to be materialized to disk, if desired. Both Fossil |
| 168 | 168 | itself, and SQLite cause the manifest file to be materialized to disk |
| 169 | 169 | so that the makefiles for these project can read the manifest and |
| 170 | 170 | embed version information in generated binaries. |
| 171 | 171 | |
| 172 | -<p>Fossil automatically generates a manifest whenever you "commit" | |
| 172 | +Fossil automatically generates a manifest whenever you "commit" | |
| 173 | 173 | a new check-in. So this is not something that you, the developer, |
| 174 | 174 | need to worry with. The format of a manifest is intentionally |
| 175 | 175 | designed to be simple to parse, so that if |
| 176 | 176 | you want to read and interpret a manifest, either by hand or |
| 177 | 177 | with a script, that is easy to do. But you will probably never |
| 178 | -need to do so.</p> | |
| 178 | +need to do so. | |
| 179 | 179 | |
| 180 | -<p>In addition to identifying all files in the check-in, a | |
| 180 | +In addition to identifying all files in the check-in, a | |
| 181 | 181 | manifest also contains a check-in comment, the date and time |
| 182 | 182 | when the check-in was established, who created the check-in, |
| 183 | 183 | and links to other check-ins from which the current check-in |
| 184 | 184 | is derived. There is also a couple of checksums used to verify |
| 185 | 185 | the integrity of the check-in. And the whole manifest might |
| 186 | -be PGP clearsigned.</p> | |
| 186 | +be PGP clearsigned. | |
| 187 | 187 | |
| 188 | 188 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 189 | 189 | |
| 190 | 190 | <ul> |
| 191 | 191 | <li>A <b>check-in</b> is a set of files arranged |
| @@ -435,29 +435,27 @@ | ||
| 435 | 435 | <h2>5.0 Setting Up A Fossil Server</h2> |
| 436 | 436 | |
| 437 | 437 | With other configuration management software, setting up a server is |
| 438 | 438 | a lot of work and normally takes time, patience, and a lot of system |
| 439 | 439 | knowledge. Fossil is designed to avoid this frustration. Setting up |
| 440 | -a server with Fossil is ridiculously easy. You have four options:</p> | |
| 441 | - | |
| 442 | -<ol> | |
| 443 | -<li><p><b>Stand-alone server.</b> | |
| 444 | -Simply run the [/help?cmd=server|fossil server] or | |
| 445 | -[/help?cmd=ui|fossil ui] command from the command-line. | |
| 446 | - | |
| 447 | -<li><p><b>CGI.</b> | |
| 448 | -Install a 2-line CGI script on a CGI-enabled web-server like Apache. | |
| 449 | - | |
| 450 | -<li><p><b>SCGI.</b> | |
| 451 | -Start an SCGI server using the | |
| 452 | -[/help?cmd=server| fossil server --scgi] command for handling | |
| 453 | -SCGI requests from web-servers like Nginx. | |
| 454 | - | |
| 455 | -<li><p><b>Inetd or Stunnel.</b> | |
| 456 | -Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests | |
| 457 | -directly to the [/help?cmd=http|fossil http] command. | |
| 458 | -</ol> | |
| 440 | +a server with Fossil is ridiculously easy. You have four options: | |
| 441 | + | |
| 442 | + # <b>Stand-alone server.</b> | |
| 443 | + Simply run the [/help?cmd=server|fossil server] or | |
| 444 | + [/help?cmd=ui|fossil ui] command from the command-line. | |
| 445 | + <br><br> | |
| 446 | + # <b>CGI.</b> | |
| 447 | + Install a 2-line CGI script on a CGI-enabled web-server like Apache. | |
| 448 | + <br><br> | |
| 449 | + # <b>SCGI.</b> | |
| 450 | + Start an SCGI server using the | |
| 451 | + [/help?cmd=server| fossil server --scgi] command for handling | |
| 452 | + SCGI requests from web-servers like Nginx. | |
| 453 | + <br><br> | |
| 454 | + # <b>Inetd or Stunnel.</b> | |
| 455 | + Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests | |
| 456 | + directly to the [/help?cmd=http|fossil http] command. | |
| 459 | 457 | |
| 460 | 458 | See the [./server/ | How To Configure A Fossil Server] document |
| 461 | 459 | for details. |
| 462 | 460 | |
| 463 | 461 | <h2>6.0 Review Of Key Concepts</h2> |
| 464 | 462 |
| --- www/concepts.wiki | |
| +++ www/concepts.wiki | |
| @@ -159,33 +159,33 @@ | |
| 159 | of the manifest is the identifier for the entire check-in. When |
| 160 | you look at a "timeline" of changes in Fossil, the ID associated |
| 161 | with each check-in or commit is really just the artifact ID of the |
| 162 | manifest for that check-in. |
| 163 | |
| 164 | <p>The manifest file is not normally a real file on disk. Instead, |
| 165 | the manifest is computed in memory by Fossil whenever it needs it. |
| 166 | However, the "fossil setting manifest on" command will cause the |
| 167 | manifest file to be materialized to disk, if desired. Both Fossil |
| 168 | itself, and SQLite cause the manifest file to be materialized to disk |
| 169 | so that the makefiles for these project can read the manifest and |
| 170 | embed version information in generated binaries. |
| 171 | |
| 172 | <p>Fossil automatically generates a manifest whenever you "commit" |
| 173 | a new check-in. So this is not something that you, the developer, |
| 174 | need to worry with. The format of a manifest is intentionally |
| 175 | designed to be simple to parse, so that if |
| 176 | you want to read and interpret a manifest, either by hand or |
| 177 | with a script, that is easy to do. But you will probably never |
| 178 | need to do so.</p> |
| 179 | |
| 180 | <p>In addition to identifying all files in the check-in, a |
| 181 | manifest also contains a check-in comment, the date and time |
| 182 | when the check-in was established, who created the check-in, |
| 183 | and links to other check-ins from which the current check-in |
| 184 | is derived. There is also a couple of checksums used to verify |
| 185 | the integrity of the check-in. And the whole manifest might |
| 186 | be PGP clearsigned.</p> |
| 187 | |
| 188 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 189 | |
| 190 | <ul> |
| 191 | <li>A <b>check-in</b> is a set of files arranged |
| @@ -435,29 +435,27 @@ | |
| 435 | <h2>5.0 Setting Up A Fossil Server</h2> |
| 436 | |
| 437 | With other configuration management software, setting up a server is |
| 438 | a lot of work and normally takes time, patience, and a lot of system |
| 439 | knowledge. Fossil is designed to avoid this frustration. Setting up |
| 440 | a server with Fossil is ridiculously easy. You have four options:</p> |
| 441 | |
| 442 | <ol> |
| 443 | <li><p><b>Stand-alone server.</b> |
| 444 | Simply run the [/help?cmd=server|fossil server] or |
| 445 | [/help?cmd=ui|fossil ui] command from the command-line. |
| 446 | |
| 447 | <li><p><b>CGI.</b> |
| 448 | Install a 2-line CGI script on a CGI-enabled web-server like Apache. |
| 449 | |
| 450 | <li><p><b>SCGI.</b> |
| 451 | Start an SCGI server using the |
| 452 | [/help?cmd=server| fossil server --scgi] command for handling |
| 453 | SCGI requests from web-servers like Nginx. |
| 454 | |
| 455 | <li><p><b>Inetd or Stunnel.</b> |
| 456 | Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests |
| 457 | directly to the [/help?cmd=http|fossil http] command. |
| 458 | </ol> |
| 459 | |
| 460 | See the [./server/ | How To Configure A Fossil Server] document |
| 461 | for details. |
| 462 | |
| 463 | <h2>6.0 Review Of Key Concepts</h2> |
| 464 |
| --- www/concepts.wiki | |
| +++ www/concepts.wiki | |
| @@ -159,33 +159,33 @@ | |
| 159 | of the manifest is the identifier for the entire check-in. When |
| 160 | you look at a "timeline" of changes in Fossil, the ID associated |
| 161 | with each check-in or commit is really just the artifact ID of the |
| 162 | manifest for that check-in. |
| 163 | |
| 164 | The manifest file is not normally a real file on disk. Instead, |
| 165 | the manifest is computed in memory by Fossil whenever it needs it. |
| 166 | However, the "fossil setting manifest on" command will cause the |
| 167 | manifest file to be materialized to disk, if desired. Both Fossil |
| 168 | itself, and SQLite cause the manifest file to be materialized to disk |
| 169 | so that the makefiles for these project can read the manifest and |
| 170 | embed version information in generated binaries. |
| 171 | |
| 172 | Fossil automatically generates a manifest whenever you "commit" |
| 173 | a new check-in. So this is not something that you, the developer, |
| 174 | need to worry with. The format of a manifest is intentionally |
| 175 | designed to be simple to parse, so that if |
| 176 | you want to read and interpret a manifest, either by hand or |
| 177 | with a script, that is easy to do. But you will probably never |
| 178 | need to do so. |
| 179 | |
| 180 | In addition to identifying all files in the check-in, a |
| 181 | manifest also contains a check-in comment, the date and time |
| 182 | when the check-in was established, who created the check-in, |
| 183 | and links to other check-ins from which the current check-in |
| 184 | is derived. There is also a couple of checksums used to verify |
| 185 | the integrity of the check-in. And the whole manifest might |
| 186 | be PGP clearsigned. |
| 187 | |
| 188 | <h3 id="keyconc">2.3 Key concepts</h3> |
| 189 | |
| 190 | <ul> |
| 191 | <li>A <b>check-in</b> is a set of files arranged |
| @@ -435,29 +435,27 @@ | |
| 435 | <h2>5.0 Setting Up A Fossil Server</h2> |
| 436 | |
| 437 | With other configuration management software, setting up a server is |
| 438 | a lot of work and normally takes time, patience, and a lot of system |
| 439 | knowledge. Fossil is designed to avoid this frustration. Setting up |
| 440 | a server with Fossil is ridiculously easy. You have four options: |
| 441 | |
| 442 | # <b>Stand-alone server.</b> |
| 443 | Simply run the [/help?cmd=server|fossil server] or |
| 444 | [/help?cmd=ui|fossil ui] command from the command-line. |
| 445 | <br><br> |
| 446 | # <b>CGI.</b> |
| 447 | Install a 2-line CGI script on a CGI-enabled web-server like Apache. |
| 448 | <br><br> |
| 449 | # <b>SCGI.</b> |
| 450 | Start an SCGI server using the |
| 451 | [/help?cmd=server| fossil server --scgi] command for handling |
| 452 | SCGI requests from web-servers like Nginx. |
| 453 | <br><br> |
| 454 | # <b>Inetd or Stunnel.</b> |
| 455 | Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests |
| 456 | directly to the [/help?cmd=http|fossil http] command. |
| 457 | |
| 458 | See the [./server/ | How To Configure A Fossil Server] document |
| 459 | for details. |
| 460 | |
| 461 | <h2>6.0 Review Of Key Concepts</h2> |
| 462 |
+184
-196
| --- www/containers.md | ||
| +++ www/containers.md | ||
| @@ -69,11 +69,11 @@ | ||
| 69 | 69 | The wrong way is to use the `Dockerfile COPY` command, because by baking |
| 70 | 70 | the repo into the image at build time, it will become one of the image’s |
| 71 | 71 | base layers. The end result is that each time you build a container from |
| 72 | 72 | that image, the repo will be reset to its build-time state. Worse, |
| 73 | 73 | restarting the container will do the same thing, since the base image |
| 74 | -layers are immutable in Docker. This is almost certainly not what you | |
| 74 | +layers are immutable. This is almost certainly not what you | |
| 75 | 75 | want. |
| 76 | 76 | |
| 77 | 77 | The correct ways put the repo into the _container_ created from the |
| 78 | 78 | _image_, not in the image itself. |
| 79 | 79 | |
| @@ -114,11 +114,11 @@ | ||
| 114 | 114 | this with `chmod`: simply reload the browser, and Fossil will try again. |
| 115 | 115 | |
| 116 | 116 | |
| 117 | 117 | ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container |
| 118 | 118 | |
| 119 | -The simple storage method above has a problem: Docker containers are | |
| 119 | +The simple storage method above has a problem: containers are | |
| 120 | 120 | designed to be killed off at the slightest cause, rebuilt, and |
| 121 | 121 | redeployed. If you do that with the repo inside the container, it gets |
| 122 | 122 | destroyed, too. The solution is to replace the “run” command above with |
| 123 | 123 | the following: |
| 124 | 124 | |
| @@ -133,12 +133,12 @@ | ||
| 133 | 133 | Because this bind mount maps a host-side directory (`~/museum`) into the |
| 134 | 134 | container, you don’t need to `docker cp` the repo into the container at |
| 135 | 135 | all. It still expects to find the repository as `repo.fossil` under that |
| 136 | 136 | directory, but now both the host and the container can see that repo DB. |
| 137 | 137 | |
| 138 | -Instead of a bind mount, you could instead set up a separate [Docker | |
| 139 | -volume](https://docs.docker.com/storage/volumes/), at which point you | |
| 138 | +Instead of a bind mount, you could instead set up a separate | |
| 139 | +[volume](https://docs.docker.com/storage/volumes/), at which point you | |
| 140 | 140 | _would_ need to `docker cp` the repo file into the container. |
| 141 | 141 | |
| 142 | 142 | Either way, files in these mounted directories have a lifetime |
| 143 | 143 | independent of the container(s) they’re mounted into. When you need to |
| 144 | 144 | rebuild the container or its underlying image — such as to upgrade to a |
| @@ -183,152 +183,25 @@ | ||
| 183 | 183 | |
| 184 | 184 | ## 3. <a id="security"></a>Security |
| 185 | 185 | |
| 186 | 186 | ### 3.1 <a id="chroot"></a>Why Not Chroot? |
| 187 | 187 | |
| 188 | -Prior to 2023.03.26, the stock Fossil container made use of [the chroot | |
| 189 | -jail feature](./chroot.md) in order to wall away the shell and other | |
| 190 | -tools provided by [BusyBox](https://www.busybox.net/BusyBox.html). This | |
| 191 | -author made a living for years in the early 1990s using Unix systems | |
| 192 | -that offered less power, so there was a legitimate worry that if someone | |
| 193 | -ever figured out how to get a shell on one of these Fossil containers, | |
| 194 | -it would constitute a powerful island from which to attack the rest of | |
| 195 | -the network. | |
| 196 | - | |
| 197 | -The thing is, Fossil is self-contained, needing none of that power in | |
| 198 | -the main-line use cases. The only reason we included BusyBox in the | |
| 199 | -container at all was on the off chance that someone needed it for | |
| 200 | -debugging. | |
| 201 | - | |
| 202 | -That justification collapsed when we realized you could restore this | |
| 203 | -basic shell environment on an as-needed basis with a one-line change to | |
| 204 | -the `Dockerfile`, as we show in the next section. | |
| 205 | - | |
| 206 | - | |
| 207 | -### 3.2 <a id="run"></a>Swapping Out the Run Layer | |
| 208 | - | |
| 209 | -If you want a basic shell environment for temporary debugging of the | |
| 210 | -running container, that’s easily added. Simply change this line in the | |
| 211 | -`Dockerfile`… | |
| 212 | - | |
| 213 | - FROM scratch AS run | |
| 214 | - | |
| 215 | -…to this: | |
| 216 | - | |
| 217 | - FROM busybox AS run | |
| 218 | - | |
| 219 | -Rebuild, redeploy, and your Fossil container now has a BusyBox based | |
| 220 | -shell environment that you can get into via: | |
| 221 | - | |
| 222 | - $ docker exec -it -u fossil $(make container-version) sh | |
| 223 | - | |
| 224 | -(That command assumes you built the container via “`make container`” and | |
| 225 | -are therefore using its versioning scheme.) | |
| 226 | - | |
| 227 | -Another case where you might need to replace this bare-bones “`run`” | |
| 228 | -layer with something more functional is that you’re setting up [email | |
| 229 | -alerts](./alerts.md) and need some way to integrate with the host’s | |
| 230 | -[MTA]. There are a number of alternatives in that linked document, so | |
| 231 | -for the sake of discussion, we’ll say you’ve chosen [Method | |
| 232 | -2](./alerts.md#db), which requires a Tcl interpreter and its SQLite | |
| 233 | -extension to push messages into the outbound email queue DB, presumably | |
| 234 | -bind-mounted into the container. | |
| 235 | - | |
| 236 | -You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` | |
| 237 | -with this: | |
| 238 | - | |
| 239 | -``` | |
| 240 | - ## --------------------------------------------------------------------- | |
| 241 | - ## STAGE 2: Pare that back to the bare essentials, plus Tcl. | |
| 242 | - ## --------------------------------------------------------------------- | |
| 243 | - FROM alpine AS run | |
| 244 | - ARG UID=499 | |
| 245 | - ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" | |
| 246 | - COPY --from=builder /tmp/fossil /bin/ | |
| 247 | - COPY tools/email-sender.tcl /bin/ | |
| 248 | - RUN set -x \ | |
| 249 | - && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ | |
| 250 | - && echo "fossil:x:${UID}:fossil" >> /etc/group \ | |
| 251 | - && install -d -m 700 -o fossil -g fossil log museum \ | |
| 252 | - && apk add --no-cache tcl sqlite-tcl | |
| 253 | -``` | |
| 254 | - | |
| 255 | -Build it and test that it works like so: | |
| 256 | - | |
| 257 | -``` | |
| 258 | - $ make container-run && | |
| 259 | - echo 'puts [info patchlevel]' | | |
| 260 | - docker exec -i $(make container-version) tclsh | |
| 261 | - 8.6.12 | |
| 262 | -``` | |
| 263 | - | |
| 264 | -You should remove the `PATH` override in the “RUN” | |
| 265 | -stage, since it’s written for the case where everything is in `/bin`. | |
| 266 | -With these additions, we need the longer `PATH` shown above to have | |
| 267 | -ready access to them all. | |
| 268 | - | |
| 269 | -Another useful case to consider is that you’ve installed a [server | |
| 270 | -extension](./serverext.wiki) and you need an interpreter for that | |
| 271 | -script. The first option above won’t work except in the unlikely case that | |
| 272 | -it’s written for one of the bare-bones script interpreters that BusyBox | |
| 273 | -ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist | |
| 274 | -shell, implementing little more than what POSIX specified in 1989, plus | |
| 275 | -equally stripped-down versions of `awk` and `sed`.) | |
| 276 | - | |
| 277 | -Let’s say the extension is written in Python. While you could handle it | |
| 278 | -the same way we do with the Tcl example above, Python is more | |
| 279 | -popular, giving us more options. Let’s inject a Python environment into | |
| 280 | -the stock Fossil container via a suitable “[distroless]” image instead: | |
| 281 | - | |
| 282 | -``` | |
| 283 | - ## --------------------------------------------------------------------- | |
| 284 | - ## STAGE 2: Pare that back to the bare essentials, plus Python. | |
| 285 | - ## --------------------------------------------------------------------- | |
| 286 | - FROM cgr.dev/chainguard/python:latest | |
| 287 | - USER root | |
| 288 | - ARG UID=499 | |
| 289 | - ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" | |
| 290 | - COPY --from=builder /tmp/fossil /bin/ | |
| 291 | - COPY --from=builder /bin/busybox.static /bin/busybox | |
| 292 | - RUN [ "/bin/busybox", "--install", "/bin" ] | |
| 293 | - RUN set -x \ | |
| 294 | - && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ | |
| 295 | - && echo "fossil:x:${UID}:fossil" >> /etc/group \ | |
| 296 | - && install -d -m 700 -o fossil -g fossil log museum | |
| 297 | -``` | |
| 298 | - | |
| 299 | -You will also have to add `busybox-static` to the APK package list in | |
| 300 | -STAGE 1 for the `RUN` script at the end of that stage to work, since the | |
| 301 | -[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to | |
| 302 | -install root-level binaries is why we change `USER` temporarily here. | |
| 303 | - | |
| 304 | -Build it and test that it works like so: | |
| 305 | - | |
| 306 | -``` | |
| 307 | - $ make container-run && | |
| 308 | - docker exec -i $(make container-version) python --version | |
| 309 | - 3.11.2 | |
| 310 | -``` | |
| 311 | - | |
| 312 | -The compensation for the hassle of using Chainguard over something more | |
| 313 | -general purpose like Alpine + “`apk add python`” | |
| 314 | -is huge: we no longer leave a package manager sitting around inside the | |
| 315 | -container, waiting for some malefactor to figure out how to abuse it. | |
| 316 | - | |
| 317 | -Beware that there’s a limit to this über-jail’s ability to save you when | |
| 318 | -you go and provide a more capable OS layer like this. The container | |
| 319 | -layer should stop an attacker from accessing any files out on the host | |
| 320 | -that you haven’t explicitly mounted into the container’s namespace, but | |
| 321 | -it can’t stop them from making outbound network connections or modifying | |
| 322 | -the repo DB inside the container. | |
| 323 | - | |
| 324 | -[cgimgs]: https://github.com/chainguard-images/images/tree/main/images | |
| 325 | -[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future | |
| 326 | -[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent | |
| 327 | - | |
| 328 | - | |
| 329 | -### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities | |
| 188 | +Prior to 2023.03.26, the stock Fossil container relied on [the chroot | |
| 189 | +jail feature](./chroot.md) to wall away the shell and other tools | |
| 190 | +provided by [BusyBox]. It included that as a bare-bones operating system | |
| 191 | +inside the container on the off chance that someone might need it for | |
| 192 | +debugging, but the thing is, Fossil is self-contained, needing none of | |
| 193 | +that power in the main-line use cases. | |
| 194 | + | |
| 195 | +Our weak “you might need it” justification collapsed when we realized | |
| 196 | +you could restore this basic shell environment with a one-line change to | |
| 197 | +the `Dockerfile`, as shown [below](#run). | |
| 198 | + | |
| 199 | +[BusyBox]: https://www.busybox.net/BusyBox.html | |
| 200 | + | |
| 201 | + | |
| 202 | +### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities | |
| 330 | 203 | |
| 331 | 204 | The example commands above create the container with [a default set of |
| 332 | 205 | Linux kernel capabilities][defcap]. Although Docker strips away almost |
| 333 | 206 | all of the traditional root capabilities by default, and Fossil doesn’t |
| 334 | 207 | need any of those it does take away, Docker does leave some enabled that |
| @@ -443,12 +316,12 @@ | ||
| 443 | 316 | |
| 444 | 317 | |
| 445 | 318 | ## 4. <a id="static"></a>Extracting a Static Binary |
| 446 | 319 | |
| 447 | 320 | Our 2-stage build process uses Alpine Linux only as a build host. Once |
| 448 | -we’ve got everything reduced to the two key static binaries — Fossil and | |
| 449 | -BusyBox — we throw all the rest of it away. | |
| 321 | +we’ve got everything reduced to a single static Fossil binary, | |
| 322 | +we throw all the rest of it away. | |
| 450 | 323 | |
| 451 | 324 | A secondary benefit falls out of this process for free: it’s arguably |
| 452 | 325 | the easiest way to build a purely static Fossil binary for Linux. Most |
| 453 | 326 | modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s |
| 454 | 327 | back-to-basics nature makes static builds work the way they used to, |
| @@ -460,17 +333,17 @@ | ||
| 460 | 333 | $ docker create --name fossil-static-tmp fossil |
| 461 | 334 | $ docker cp fossil-static-tmp:/bin/fossil . |
| 462 | 335 | $ docker container rm fossil-static-tmp |
| 463 | 336 | ``` |
| 464 | 337 | |
| 465 | -The resulting binary is the single largest file inside that container, | |
| 466 | -at about 6 MiB. (It’s built stripped.) | |
| 338 | +The result is six or seven megs, depending on the CPU architecture you | |
| 339 | +build for. It’s built stripped. | |
| 467 | 340 | |
| 468 | 341 | [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 469 | 342 | |
| 470 | 343 | |
| 471 | -## 5. <a id="args"></a>Container Build Arguments | |
| 344 | +## 5. <a id="custom" name="args"></a>Customization Points | |
| 472 | 345 | |
| 473 | 346 | ### <a id="pkg-vers"></a> 5.1 Fossil Version |
| 474 | 347 | |
| 475 | 348 | The default version of Fossil fetched in the build is the version in the |
| 476 | 349 | checkout directory at the time you run it. You could override it to get |
| @@ -486,21 +359,21 @@ | ||
| 486 | 359 | $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20' |
| 487 | 360 | ``` |
| 488 | 361 | |
| 489 | 362 | While you could instead use the generic |
| 490 | 363 | “`release`” tag here, it’s better to use a specific version number |
| 491 | -since Docker caches downloaded files and tries to | |
| 364 | +since container builders cache downloaded files, hoping to | |
| 492 | 365 | reuse them across builds. If you ask for “`release`” before a new |
| 493 | 366 | version is tagged and then immediately after, you might expect to get |
| 494 | 367 | two different tarballs, but because the underlying source tarball URL |
| 495 | 368 | remains the same when you do that, you’ll end up reusing the |
| 496 | -old tarball from your Docker cache. This will occur | |
| 369 | +old tarball from cache. This will occur | |
| 497 | 370 | even if you pass the “`docker build --no-cache`” option. |
| 498 | 371 | |
| 499 | 372 | This is why we default to pulling the Fossil tarball by checkin ID |
| 500 | 373 | rather than let it default to the generic “`trunk`” tag: so the URL will |
| 501 | -change each time you update your Fossil source tree, forcing Docker to | |
| 374 | +change each time you update your Fossil source tree, forcing the builder to | |
| 502 | 375 | pull a fresh tarball. |
| 503 | 376 | |
| 504 | 377 | |
| 505 | 378 | ### 5.2 <a id="uids"></a>User & Group IDs |
| 506 | 379 | |
| @@ -512,22 +385,34 @@ | ||
| 512 | 385 | close to zero as we can manage. |
| 513 | 386 | |
| 514 | 387 | To change it to something else, say: |
| 515 | 388 | |
| 516 | 389 | ``` |
| 517 | - $ make container-image \ | |
| 518 | - DBFLAGS='--build-arg UID=501' | |
| 390 | + $ make container-image DBFLAGS='--build-arg UID=501' | |
| 519 | 391 | ``` |
| 520 | 392 | |
| 521 | 393 | This is particularly useful if you’re putting your repository on a |
| 522 | -Docker volume since the IDs “leak” out into the host environment via | |
| 394 | +separate volume since the IDs “leak” out into the host environment via | |
| 523 | 395 | file permissions. You may therefore wish them to mean something on both |
| 524 | 396 | sides of the container barrier rather than have “499” appear on the host |
| 525 | 397 | in “`ls -l`” output. |
| 526 | 398 | |
| 527 | 399 | |
| 528 | -### 5.3 <a id="config"></a>Fossil Configuration Options | |
| 400 | +### 5.3 <a id="cengine"></a>Container Engine | |
| 401 | + | |
| 402 | +Although the Fossil container build system defaults to Docker, we allow | |
| 403 | +for use of any OCI container system that implements the same interfaces. | |
| 404 | +We go into more details about this [below](#light), but | |
| 405 | +for now, it suffices to point out that you can switch to Podman while | |
| 406 | +using our `Makefile` convenience targets unchanged by saying: | |
| 407 | + | |
| 408 | +``` | |
| 409 | + $ make CENGINE=podman container-run | |
| 410 | +``` | |
| 411 | + | |
| 412 | + | |
| 413 | +### 5.4 <a id="config"></a>Fossil Configuration Options | |
| 529 | 414 | |
| 530 | 415 | You can use this same mechanism to enable non-default Fossil |
| 531 | 416 | configuration options in your build. For instance, to turn on |
| 532 | 417 | the JSON API and the TH1 docs extension: |
| 533 | 418 | |
| @@ -535,31 +420,133 @@ | ||
| 535 | 420 | $ make container-image \ |
| 536 | 421 | DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"' |
| 537 | 422 | ``` |
| 538 | 423 | |
| 539 | 424 | If you also wanted [the Tcl evaluation extension](./th1.md#tclEval), |
| 540 | -that’s trickier because it requires the `tcl-dev` package to be | |
| 541 | -installed atop Alpine Linux in the first image build stage. We don’t | |
| 542 | -currently have a way to do that because it brings you to a new problem: | |
| 543 | -Alpine provides only a dynamic Tcl library, which conflicts with our | |
| 544 | -wish for [a static Fossil binary](#static). For those who want such a | |
| 545 | -“batteries included” container, we recommend taking a look at [this | |
| 546 | -alternative](https://hub.docker.com/r/duvel/fossil); needless to say, | |
| 547 | -it’s inherently less secure than our stock container, but you may find | |
| 548 | -the tradeoff worthwhile. | |
| 549 | - | |
| 550 | -### 5.4 <a id="cengine"></a>Container Engine | |
| 551 | - | |
| 552 | -Although the Fossil container build system defaults to Docker, we allow | |
| 553 | -for use of any OCI container system that implements the same interfaces. | |
| 554 | -We go into more details about this in [the next section](#light), but | |
| 555 | -for now, it suffices to point out that you can switch to Podman while | |
| 556 | -using our `Makefile` convenience targets unchanged by saying: | |
| 557 | - | |
| 558 | -``` | |
| 559 | - $ make CENGINE=podman container-run | |
| 560 | -``` | |
| 425 | +that brings us to [the next point](#run). | |
| 426 | + | |
| 427 | + | |
| 428 | +### 5.5 <a id="run"></a>Elaborating the Run Layer | |
| 429 | + | |
| 430 | +If you want a basic shell environment for temporary debugging of the | |
| 431 | +running container, that’s easily added. Simply change this line in the | |
| 432 | +`Dockerfile`… | |
| 433 | + | |
| 434 | + FROM scratch AS run | |
| 435 | + | |
| 436 | +…to this: | |
| 437 | + | |
| 438 | + FROM busybox AS run | |
| 439 | + | |
| 440 | +Rebuild and redeploy to give your Fossil container a [BusyBox]-based | |
| 441 | +shell environment that you can get into via: | |
| 442 | + | |
| 443 | + $ docker exec -it -u fossil $(make container-version) sh | |
| 444 | + | |
| 445 | +(That command assumes you built it via “`make container`” and are | |
| 446 | +therefore using its versioning scheme.) | |
| 447 | + | |
| 448 | +Another case where you might need to replace this bare-bones “`run`” | |
| 449 | +layer with something more functional is that you’re setting up [email | |
| 450 | +alerts](./alerts.md) and need some way to integrate with the host’s | |
| 451 | +[MTA]. There are a number of alternatives in that linked document, so | |
| 452 | +for the sake of discussion, we’ll say you’ve chosen [Method | |
| 453 | +2](./alerts.md#db), which requires a Tcl interpreter and its SQLite | |
| 454 | +extension to push messages into the outbound email queue DB, presumably | |
| 455 | +bind-mounted into the container. | |
| 456 | + | |
| 457 | +You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` | |
| 458 | +with this: | |
| 459 | + | |
| 460 | +``` | |
| 461 | + ## --------------------------------------------------------------------- | |
| 462 | + ## STAGE 2: Pare that back to the bare essentials, plus Tcl. | |
| 463 | + ## --------------------------------------------------------------------- | |
| 464 | + FROM alpine AS run | |
| 465 | + ARG UID=499 | |
| 466 | + ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" | |
| 467 | + COPY --from=builder /tmp/fossil /bin/ | |
| 468 | + COPY tools/email-sender.tcl /bin/ | |
| 469 | + RUN set -x \ | |
| 470 | + && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ | |
| 471 | + && echo "fossil:x:${UID}:fossil" >> /etc/group \ | |
| 472 | + && install -d -m 700 -o fossil -g fossil log museum \ | |
| 473 | + && apk add --no-cache tcl sqlite-tcl | |
| 474 | +``` | |
| 475 | + | |
| 476 | +Build it and test that it works like so: | |
| 477 | + | |
| 478 | +``` | |
| 479 | + $ make container-run && | |
| 480 | + echo 'puts [info patchlevel]' | | |
| 481 | + docker exec -i $(make container-version) tclsh | |
| 482 | + 8.6.12 | |
| 483 | +``` | |
| 484 | + | |
| 485 | +You should remove the `PATH` override in the “RUN” | |
| 486 | +stage, since it’s written for the case where everything is in `/bin`. | |
| 487 | +With these additions, we need the longer `PATH` shown above to have | |
| 488 | +ready access to them all. | |
| 489 | + | |
| 490 | +Another useful case to consider is that you’ve installed a [server | |
| 491 | +extension](./serverext.wiki) and you need an interpreter for that | |
| 492 | +script. The first option above won’t work except in the unlikely case that | |
| 493 | +it’s written for one of the bare-bones script interpreters that BusyBox | |
| 494 | +ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist | |
| 495 | +shell, implementing little more than what POSIX specified in 1989, plus | |
| 496 | +equally stripped-down versions of `awk` and `sed`.) | |
| 497 | + | |
| 498 | +Let’s say the extension is written in Python. While you could handle it | |
| 499 | +the same way we do with the Tcl example above, Python is more | |
| 500 | +popular, giving us more options. Let’s inject a Python environment into | |
| 501 | +the stock Fossil container via a suitable “[distroless]” image instead: | |
| 502 | + | |
| 503 | +``` | |
| 504 | + ## --------------------------------------------------------------------- | |
| 505 | + ## STAGE 2: Pare that back to the bare essentials, plus Python. | |
| 506 | + ## --------------------------------------------------------------------- | |
| 507 | + FROM cgr.dev/chainguard/python:latest | |
| 508 | + USER root | |
| 509 | + ARG UID=499 | |
| 510 | + ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" | |
| 511 | + COPY --from=builder /tmp/fossil /bin/ | |
| 512 | + COPY --from=builder /bin/busybox.static /bin/busybox | |
| 513 | + RUN [ "/bin/busybox", "--install", "/bin" ] | |
| 514 | + RUN set -x \ | |
| 515 | + && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ | |
| 516 | + && echo "fossil:x:${UID}:fossil" >> /etc/group \ | |
| 517 | + && install -d -m 700 -o fossil -g fossil log museum | |
| 518 | +``` | |
| 519 | + | |
| 520 | +You will also have to add `busybox-static` to the APK package list in | |
| 521 | +STAGE 1 for the `RUN` script at the end of that stage to work, since the | |
| 522 | +[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to | |
| 523 | +install root-level binaries is why we change `USER` temporarily here. | |
| 524 | + | |
| 525 | +Build it and test that it works like so: | |
| 526 | + | |
| 527 | +``` | |
| 528 | + $ make container-run && | |
| 529 | + docker exec -i $(make container-version) python --version | |
| 530 | + 3.11.2 | |
| 531 | +``` | |
| 532 | + | |
| 533 | +The compensation for the hassle of using Chainguard over something more | |
| 534 | +general purpose like Alpine + “`apk add python`” | |
| 535 | +is huge: we no longer leave a package manager sitting around inside the | |
| 536 | +container, waiting for some malefactor to figure out how to abuse it. | |
| 537 | + | |
| 538 | +Beware that there’s a limit to this über-jail’s ability to save you when | |
| 539 | +you go and provide a more capable OS layer like this. The container | |
| 540 | +layer should stop an attacker from accessing any files out on the host | |
| 541 | +that you haven’t explicitly mounted into the container’s namespace, but | |
| 542 | +it can’t stop them from making outbound network connections or modifying | |
| 543 | +the repo DB inside the container. | |
| 544 | + | |
| 545 | +[cgimgs]: https://github.com/chainguard-images/images/tree/main/images | |
| 546 | +[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future | |
| 547 | +[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent | |
| 561 | 548 | |
| 562 | 549 | |
| 563 | 550 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 564 | 551 | |
| 565 | 552 | Those afflicted with sticker shock at seeing the size of a [Docker |
| @@ -567,13 +554,13 @@ | ||
| 567 | 554 | “noped” out of the whole concept of containers. The first thing to |
| 568 | 555 | realize is that when it comes to actually serving simple containers like |
| 569 | 556 | the ones shown above is that [Docker Engine][DE] suffices, at about a |
| 570 | 557 | quarter of the size. |
| 571 | 558 | |
| 572 | -Yet on a small server — say, a $4/month 10 GiB Digital Ocean droplet — | |
| 573 | -that’s still a big chunk of your storage budget. It takes 100:1 overhead | |
| 574 | -just to run a 4 MiB Fossil server container? Once again, I wouldn’t | |
| 559 | +Yet on a small server — say, a $4/month ten gig Digital Ocean droplet — | |
| 560 | +that’s still a big chunk of your storage budget. It takes ~60:1 overhead | |
| 561 | +merely to run a Fossil server container? Once again, I wouldn’t | |
| 575 | 562 | blame you if you noped right on out of here, but if you will be patient, |
| 576 | 563 | you will find that there are ways to run Fossil inside a container even |
| 577 | 564 | on entry-level cloud VPSes. These are well-suited to running Fossil; you |
| 578 | 565 | don’t have to resort to [raw Fossil service][srv] to succeed, |
| 579 | 566 | leaving the benefits of containerization to those with bigger budgets. |
| @@ -587,11 +574,11 @@ | ||
| 587 | 574 | --publish 127.0.0.1:9999:8080 |
| 588 | 575 | ``` |
| 589 | 576 | |
| 590 | 577 | The assumption is that there’s a reverse proxy running somewhere that |
| 591 | 578 | redirects public web hits to localhost port 9999, which in turn goes to |
| 592 | -port 8080 inside the container. This use of Docker/Podman port | |
| 579 | +port 8080 inside the container. This use of port | |
| 593 | 580 | publishing effectively replaces the use of the |
| 594 | 581 | “`fossil server --localhost`” option. |
| 595 | 582 | |
| 596 | 583 | For the nginx case, you need to add `--scgi` to these commands, and you |
| 597 | 584 | might also need to specify `--baseurl`. |
| @@ -616,17 +603,18 @@ | ||
| 616 | 603 | |
| 617 | 604 | |
| 618 | 605 | ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down |
| 619 | 606 | |
| 620 | 607 | The core of Docker Engine is its [`containerd`][ctrd] daemon and the |
| 621 | -[`runc`][runc] container runner. Add to this the out-of-core CLI program | |
| 608 | +[`runc`][runc] container runtime. Add to this the out-of-core CLI program | |
| 622 | 609 | [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil |
| 623 | 610 | containers. The big things you’re missing are: |
| 624 | 611 | |
| 625 | 612 | * **BuildKit**: The container build engine, which doesn’t matter if |
| 626 | - you’re building elsewhere and using a container registry as an | |
| 627 | - intermediary between that build host and the deployment host. | |
| 613 | + you’re building elsewhere and shipping the images to the target. | |
| 614 | + A good example is using a container registry as an | |
| 615 | + intermediary between the build and deployment hosts. | |
| 628 | 616 | |
| 629 | 617 | * **SwarmKit**: A powerful yet simple orchestrator for Docker that you |
| 630 | 618 | probably aren’t using with Fossil anyway. |
| 631 | 619 | |
| 632 | 620 | In exchange, you get a runtime that’s about half the size of Docker |
| @@ -640,12 +628,12 @@ | ||
| 640 | 628 | [runc]: https://github.com/opencontainers/runc |
| 641 | 629 | |
| 642 | 630 | |
| 643 | 631 | ### 6.2 <a id="podman"></a>Podman |
| 644 | 632 | |
| 645 | -A lighter-weight [rootless] [drop-in replacement][whatis] that doesn’t | |
| 646 | -give up the image builder is [Podman]. Initially created by | |
| 633 | +A lighter-weight [rootless][rl] [drop-in replacement][whatis] that | |
| 634 | +doesn’t give up the image builder is [Podman]. Initially created by | |
| 647 | 635 | Red Hat and thus popular on that family of OSes, it will run on |
| 648 | 636 | any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] |
| 649 | 637 | or [on Windows via WSL2][pmwin]. |
| 650 | 638 | |
| 651 | 639 | On Ubuntu 22.04, the installation size is about 38 MiB, roughly a |
| @@ -680,15 +668,15 @@ | ||
| 680 | 668 | --publish 127.0.0.1:9999:8080 \ |
| 681 | 669 | localhost/fossil |
| 682 | 670 | $ podman start fossil |
| 683 | 671 | ``` |
| 684 | 672 | |
| 685 | -[pmmac]: https://podman.io/getting-started/installation.html#macos | |
| 686 | -[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md | |
| 687 | -[Podman]: https://podman.io/ | |
| 688 | -[rootless]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md | |
| 689 | -[whatis]: https://podman.io/whatis.html | |
| 673 | +[pmmac]: https://podman.io/getting-started/installation.html#macos | |
| 674 | +[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md | |
| 675 | +[Podman]: https://podman.io/ | |
| 676 | +[rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md | |
| 677 | +[whatis]: https://podman.io/whatis.html | |
| 690 | 678 | |
| 691 | 679 | |
| 692 | 680 | ### 6.3 <a id="nspawn"></a>`systemd-container` |
| 693 | 681 | |
| 694 | 682 | If even the Podman stack is too big for you, the next-best option I’m |
| @@ -788,11 +776,11 @@ | ||
| 788 | 776 | * The path in the host-side part of the `Bind` value must point at the |
| 789 | 777 | directory containing the `repo.fossil` file referenced in said |
| 790 | 778 | command so that `/museum/repo.fossil` refers to your repo out |
| 791 | 779 | on the host for the reasons given [above](#bind-mount). |
| 792 | 780 | |
| 793 | -That being done, we also need a generic systemd unit file called | |
| 781 | +That being done, we also need a generic `systemd` unit file called | |
| 794 | 782 | `/etc/systemd/system/[email protected]`, containing: |
| 795 | 783 | |
| 796 | 784 | ---- |
| 797 | 785 | |
| 798 | 786 | ``` |
| @@ -841,11 +829,11 @@ | ||
| 841 | 829 | ``` |
| 842 | 830 | |
| 843 | 831 | You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability |
| 844 | 832 | to allow Fossil to bind to this low-numbered port. |
| 845 | 833 | |
| 846 | -We use systemd’s template file feature to allow multiple Fossil | |
| 834 | +We use the `systemd` template file feature to allow multiple Fossil | |
| 847 | 835 | servers running on a single machine, each on a different TCP port, |
| 848 | 836 | as when proxying them out as subdirectories of a larger site. |
| 849 | 837 | To add another project, you must first clone the base “machine” layer: |
| 850 | 838 | |
| 851 | 839 | ``` |
| @@ -996,11 +984,11 @@ | ||
| 996 | 984 | you’re serving Fossil from, you may need to know which assumptions |
| 997 | 985 | our container violates and the resulting consequences. |
| 998 | 986 | |
| 999 | 987 | Some of it we discussed above already, but there’s one big class of |
| 1000 | 988 | problems we haven’t covered yet. It stems from the fact that our stock |
| 1001 | -container starts a single static executable inside a barebones container | |
| 989 | +container starts a single static executable inside a bare-bones container | |
| 1002 | 990 | rather than “boot” an OS image. That causes a bunch of commands to fail: |
| 1003 | 991 | |
| 1004 | 992 | * **`machinectl poweroff`** will fail because the container |
| 1005 | 993 | isn’t running dbus. |
| 1006 | 994 | |
| 1007 | 995 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -69,11 +69,11 @@ | |
| 69 | The wrong way is to use the `Dockerfile COPY` command, because by baking |
| 70 | the repo into the image at build time, it will become one of the image’s |
| 71 | base layers. The end result is that each time you build a container from |
| 72 | that image, the repo will be reset to its build-time state. Worse, |
| 73 | restarting the container will do the same thing, since the base image |
| 74 | layers are immutable in Docker. This is almost certainly not what you |
| 75 | want. |
| 76 | |
| 77 | The correct ways put the repo into the _container_ created from the |
| 78 | _image_, not in the image itself. |
| 79 | |
| @@ -114,11 +114,11 @@ | |
| 114 | this with `chmod`: simply reload the browser, and Fossil will try again. |
| 115 | |
| 116 | |
| 117 | ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container |
| 118 | |
| 119 | The simple storage method above has a problem: Docker containers are |
| 120 | designed to be killed off at the slightest cause, rebuilt, and |
| 121 | redeployed. If you do that with the repo inside the container, it gets |
| 122 | destroyed, too. The solution is to replace the “run” command above with |
| 123 | the following: |
| 124 | |
| @@ -133,12 +133,12 @@ | |
| 133 | Because this bind mount maps a host-side directory (`~/museum`) into the |
| 134 | container, you don’t need to `docker cp` the repo into the container at |
| 135 | all. It still expects to find the repository as `repo.fossil` under that |
| 136 | directory, but now both the host and the container can see that repo DB. |
| 137 | |
| 138 | Instead of a bind mount, you could instead set up a separate [Docker |
| 139 | volume](https://docs.docker.com/storage/volumes/), at which point you |
| 140 | _would_ need to `docker cp` the repo file into the container. |
| 141 | |
| 142 | Either way, files in these mounted directories have a lifetime |
| 143 | independent of the container(s) they’re mounted into. When you need to |
| 144 | rebuild the container or its underlying image — such as to upgrade to a |
| @@ -183,152 +183,25 @@ | |
| 183 | |
| 184 | ## 3. <a id="security"></a>Security |
| 185 | |
| 186 | ### 3.1 <a id="chroot"></a>Why Not Chroot? |
| 187 | |
| 188 | Prior to 2023.03.26, the stock Fossil container made use of [the chroot |
| 189 | jail feature](./chroot.md) in order to wall away the shell and other |
| 190 | tools provided by [BusyBox](https://www.busybox.net/BusyBox.html). This |
| 191 | author made a living for years in the early 1990s using Unix systems |
| 192 | that offered less power, so there was a legitimate worry that if someone |
| 193 | ever figured out how to get a shell on one of these Fossil containers, |
| 194 | it would constitute a powerful island from which to attack the rest of |
| 195 | the network. |
| 196 | |
| 197 | The thing is, Fossil is self-contained, needing none of that power in |
| 198 | the main-line use cases. The only reason we included BusyBox in the |
| 199 | container at all was on the off chance that someone needed it for |
| 200 | debugging. |
| 201 | |
| 202 | That justification collapsed when we realized you could restore this |
| 203 | basic shell environment on an as-needed basis with a one-line change to |
| 204 | the `Dockerfile`, as we show in the next section. |
| 205 | |
| 206 | |
| 207 | ### 3.2 <a id="run"></a>Swapping Out the Run Layer |
| 208 | |
| 209 | If you want a basic shell environment for temporary debugging of the |
| 210 | running container, that’s easily added. Simply change this line in the |
| 211 | `Dockerfile`… |
| 212 | |
| 213 | FROM scratch AS run |
| 214 | |
| 215 | …to this: |
| 216 | |
| 217 | FROM busybox AS run |
| 218 | |
| 219 | Rebuild, redeploy, and your Fossil container now has a BusyBox based |
| 220 | shell environment that you can get into via: |
| 221 | |
| 222 | $ docker exec -it -u fossil $(make container-version) sh |
| 223 | |
| 224 | (That command assumes you built the container via “`make container`” and |
| 225 | are therefore using its versioning scheme.) |
| 226 | |
| 227 | Another case where you might need to replace this bare-bones “`run`” |
| 228 | layer with something more functional is that you’re setting up [email |
| 229 | alerts](./alerts.md) and need some way to integrate with the host’s |
| 230 | [MTA]. There are a number of alternatives in that linked document, so |
| 231 | for the sake of discussion, we’ll say you’ve chosen [Method |
| 232 | 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite |
| 233 | extension to push messages into the outbound email queue DB, presumably |
| 234 | bind-mounted into the container. |
| 235 | |
| 236 | You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` |
| 237 | with this: |
| 238 | |
| 239 | ``` |
| 240 | ## --------------------------------------------------------------------- |
| 241 | ## STAGE 2: Pare that back to the bare essentials, plus Tcl. |
| 242 | ## --------------------------------------------------------------------- |
| 243 | FROM alpine AS run |
| 244 | ARG UID=499 |
| 245 | ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" |
| 246 | COPY --from=builder /tmp/fossil /bin/ |
| 247 | COPY tools/email-sender.tcl /bin/ |
| 248 | RUN set -x \ |
| 249 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ |
| 250 | && echo "fossil:x:${UID}:fossil" >> /etc/group \ |
| 251 | && install -d -m 700 -o fossil -g fossil log museum \ |
| 252 | && apk add --no-cache tcl sqlite-tcl |
| 253 | ``` |
| 254 | |
| 255 | Build it and test that it works like so: |
| 256 | |
| 257 | ``` |
| 258 | $ make container-run && |
| 259 | echo 'puts [info patchlevel]' | |
| 260 | docker exec -i $(make container-version) tclsh |
| 261 | 8.6.12 |
| 262 | ``` |
| 263 | |
| 264 | You should remove the `PATH` override in the “RUN” |
| 265 | stage, since it’s written for the case where everything is in `/bin`. |
| 266 | With these additions, we need the longer `PATH` shown above to have |
| 267 | ready access to them all. |
| 268 | |
| 269 | Another useful case to consider is that you’ve installed a [server |
| 270 | extension](./serverext.wiki) and you need an interpreter for that |
| 271 | script. The first option above won’t work except in the unlikely case that |
| 272 | it’s written for one of the bare-bones script interpreters that BusyBox |
| 273 | ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist |
| 274 | shell, implementing little more than what POSIX specified in 1989, plus |
| 275 | equally stripped-down versions of `awk` and `sed`.) |
| 276 | |
| 277 | Let’s say the extension is written in Python. While you could handle it |
| 278 | the same way we do with the Tcl example above, Python is more |
| 279 | popular, giving us more options. Let’s inject a Python environment into |
| 280 | the stock Fossil container via a suitable “[distroless]” image instead: |
| 281 | |
| 282 | ``` |
| 283 | ## --------------------------------------------------------------------- |
| 284 | ## STAGE 2: Pare that back to the bare essentials, plus Python. |
| 285 | ## --------------------------------------------------------------------- |
| 286 | FROM cgr.dev/chainguard/python:latest |
| 287 | USER root |
| 288 | ARG UID=499 |
| 289 | ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" |
| 290 | COPY --from=builder /tmp/fossil /bin/ |
| 291 | COPY --from=builder /bin/busybox.static /bin/busybox |
| 292 | RUN [ "/bin/busybox", "--install", "/bin" ] |
| 293 | RUN set -x \ |
| 294 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ |
| 295 | && echo "fossil:x:${UID}:fossil" >> /etc/group \ |
| 296 | && install -d -m 700 -o fossil -g fossil log museum |
| 297 | ``` |
| 298 | |
| 299 | You will also have to add `busybox-static` to the APK package list in |
| 300 | STAGE 1 for the `RUN` script at the end of that stage to work, since the |
| 301 | [Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to |
| 302 | install root-level binaries is why we change `USER` temporarily here. |
| 303 | |
| 304 | Build it and test that it works like so: |
| 305 | |
| 306 | ``` |
| 307 | $ make container-run && |
| 308 | docker exec -i $(make container-version) python --version |
| 309 | 3.11.2 |
| 310 | ``` |
| 311 | |
| 312 | The compensation for the hassle of using Chainguard over something more |
| 313 | general purpose like Alpine + “`apk add python`” |
| 314 | is huge: we no longer leave a package manager sitting around inside the |
| 315 | container, waiting for some malefactor to figure out how to abuse it. |
| 316 | |
| 317 | Beware that there’s a limit to this über-jail’s ability to save you when |
| 318 | you go and provide a more capable OS layer like this. The container |
| 319 | layer should stop an attacker from accessing any files out on the host |
| 320 | that you haven’t explicitly mounted into the container’s namespace, but |
| 321 | it can’t stop them from making outbound network connections or modifying |
| 322 | the repo DB inside the container. |
| 323 | |
| 324 | [cgimgs]: https://github.com/chainguard-images/images/tree/main/images |
| 325 | [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future |
| 326 | [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent |
| 327 | |
| 328 | |
| 329 | ### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities |
| 330 | |
| 331 | The example commands above create the container with [a default set of |
| 332 | Linux kernel capabilities][defcap]. Although Docker strips away almost |
| 333 | all of the traditional root capabilities by default, and Fossil doesn’t |
| 334 | need any of those it does take away, Docker does leave some enabled that |
| @@ -443,12 +316,12 @@ | |
| 443 | |
| 444 | |
| 445 | ## 4. <a id="static"></a>Extracting a Static Binary |
| 446 | |
| 447 | Our 2-stage build process uses Alpine Linux only as a build host. Once |
| 448 | we’ve got everything reduced to the two key static binaries — Fossil and |
| 449 | BusyBox — we throw all the rest of it away. |
| 450 | |
| 451 | A secondary benefit falls out of this process for free: it’s arguably |
| 452 | the easiest way to build a purely static Fossil binary for Linux. Most |
| 453 | modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s |
| 454 | back-to-basics nature makes static builds work the way they used to, |
| @@ -460,17 +333,17 @@ | |
| 460 | $ docker create --name fossil-static-tmp fossil |
| 461 | $ docker cp fossil-static-tmp:/bin/fossil . |
| 462 | $ docker container rm fossil-static-tmp |
| 463 | ``` |
| 464 | |
| 465 | The resulting binary is the single largest file inside that container, |
| 466 | at about 6 MiB. (It’s built stripped.) |
| 467 | |
| 468 | [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 469 | |
| 470 | |
| 471 | ## 5. <a id="args"></a>Container Build Arguments |
| 472 | |
| 473 | ### <a id="pkg-vers"></a> 5.1 Fossil Version |
| 474 | |
| 475 | The default version of Fossil fetched in the build is the version in the |
| 476 | checkout directory at the time you run it. You could override it to get |
| @@ -486,21 +359,21 @@ | |
| 486 | $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20' |
| 487 | ``` |
| 488 | |
| 489 | While you could instead use the generic |
| 490 | “`release`” tag here, it’s better to use a specific version number |
| 491 | since Docker caches downloaded files and tries to |
| 492 | reuse them across builds. If you ask for “`release`” before a new |
| 493 | version is tagged and then immediately after, you might expect to get |
| 494 | two different tarballs, but because the underlying source tarball URL |
| 495 | remains the same when you do that, you’ll end up reusing the |
| 496 | old tarball from your Docker cache. This will occur |
| 497 | even if you pass the “`docker build --no-cache`” option. |
| 498 | |
| 499 | This is why we default to pulling the Fossil tarball by checkin ID |
| 500 | rather than let it default to the generic “`trunk`” tag: so the URL will |
| 501 | change each time you update your Fossil source tree, forcing Docker to |
| 502 | pull a fresh tarball. |
| 503 | |
| 504 | |
| 505 | ### 5.2 <a id="uids"></a>User & Group IDs |
| 506 | |
| @@ -512,22 +385,34 @@ | |
| 512 | close to zero as we can manage. |
| 513 | |
| 514 | To change it to something else, say: |
| 515 | |
| 516 | ``` |
| 517 | $ make container-image \ |
| 518 | DBFLAGS='--build-arg UID=501' |
| 519 | ``` |
| 520 | |
| 521 | This is particularly useful if you’re putting your repository on a |
| 522 | Docker volume since the IDs “leak” out into the host environment via |
| 523 | file permissions. You may therefore wish them to mean something on both |
| 524 | sides of the container barrier rather than have “499” appear on the host |
| 525 | in “`ls -l`” output. |
| 526 | |
| 527 | |
| 528 | ### 5.3 <a id="config"></a>Fossil Configuration Options |
| 529 | |
| 530 | You can use this same mechanism to enable non-default Fossil |
| 531 | configuration options in your build. For instance, to turn on |
| 532 | the JSON API and the TH1 docs extension: |
| 533 | |
| @@ -535,31 +420,133 @@ | |
| 535 | $ make container-image \ |
| 536 | DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"' |
| 537 | ``` |
| 538 | |
| 539 | If you also wanted [the Tcl evaluation extension](./th1.md#tclEval), |
| 540 | that’s trickier because it requires the `tcl-dev` package to be |
| 541 | installed atop Alpine Linux in the first image build stage. We don’t |
| 542 | currently have a way to do that because it brings you to a new problem: |
| 543 | Alpine provides only a dynamic Tcl library, which conflicts with our |
| 544 | wish for [a static Fossil binary](#static). For those who want such a |
| 545 | “batteries included” container, we recommend taking a look at [this |
| 546 | alternative](https://hub.docker.com/r/duvel/fossil); needless to say, |
| 547 | it’s inherently less secure than our stock container, but you may find |
| 548 | the tradeoff worthwhile. |
| 549 | |
| 550 | ### 5.4 <a id="cengine"></a>Container Engine |
| 551 | |
| 552 | Although the Fossil container build system defaults to Docker, we allow |
| 553 | for use of any OCI container system that implements the same interfaces. |
| 554 | We go into more details about this in [the next section](#light), but |
| 555 | for now, it suffices to point out that you can switch to Podman while |
| 556 | using our `Makefile` convenience targets unchanged by saying: |
| 557 | |
| 558 | ``` |
| 559 | $ make CENGINE=podman container-run |
| 560 | ``` |
| 561 | |
| 562 | |
| 563 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 564 | |
| 565 | Those afflicted with sticker shock at seeing the size of a [Docker |
| @@ -567,13 +554,13 @@ | |
| 567 | “noped” out of the whole concept of containers. The first thing to |
| 568 | realize is that when it comes to actually serving simple containers like |
| 569 | the ones shown above is that [Docker Engine][DE] suffices, at about a |
| 570 | quarter of the size. |
| 571 | |
| 572 | Yet on a small server — say, a $4/month 10 GiB Digital Ocean droplet — |
| 573 | that’s still a big chunk of your storage budget. It takes 100:1 overhead |
| 574 | just to run a 4 MiB Fossil server container? Once again, I wouldn’t |
| 575 | blame you if you noped right on out of here, but if you will be patient, |
| 576 | you will find that there are ways to run Fossil inside a container even |
| 577 | on entry-level cloud VPSes. These are well-suited to running Fossil; you |
| 578 | don’t have to resort to [raw Fossil service][srv] to succeed, |
| 579 | leaving the benefits of containerization to those with bigger budgets. |
| @@ -587,11 +574,11 @@ | |
| 587 | --publish 127.0.0.1:9999:8080 |
| 588 | ``` |
| 589 | |
| 590 | The assumption is that there’s a reverse proxy running somewhere that |
| 591 | redirects public web hits to localhost port 9999, which in turn goes to |
| 592 | port 8080 inside the container. This use of Docker/Podman port |
| 593 | publishing effectively replaces the use of the |
| 594 | “`fossil server --localhost`” option. |
| 595 | |
| 596 | For the nginx case, you need to add `--scgi` to these commands, and you |
| 597 | might also need to specify `--baseurl`. |
| @@ -616,17 +603,18 @@ | |
| 616 | |
| 617 | |
| 618 | ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down |
| 619 | |
| 620 | The core of Docker Engine is its [`containerd`][ctrd] daemon and the |
| 621 | [`runc`][runc] container runner. Add to this the out-of-core CLI program |
| 622 | [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil |
| 623 | containers. The big things you’re missing are: |
| 624 | |
| 625 | * **BuildKit**: The container build engine, which doesn’t matter if |
| 626 | you’re building elsewhere and using a container registry as an |
| 627 | intermediary between that build host and the deployment host. |
| 628 | |
| 629 | * **SwarmKit**: A powerful yet simple orchestrator for Docker that you |
| 630 | probably aren’t using with Fossil anyway. |
| 631 | |
| 632 | In exchange, you get a runtime that’s about half the size of Docker |
| @@ -640,12 +628,12 @@ | |
| 640 | [runc]: https://github.com/opencontainers/runc |
| 641 | |
| 642 | |
| 643 | ### 6.2 <a id="podman"></a>Podman |
| 644 | |
| 645 | A lighter-weight [rootless] [drop-in replacement][whatis] that doesn’t |
| 646 | give up the image builder is [Podman]. Initially created by |
| 647 | Red Hat and thus popular on that family of OSes, it will run on |
| 648 | any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] |
| 649 | or [on Windows via WSL2][pmwin]. |
| 650 | |
| 651 | On Ubuntu 22.04, the installation size is about 38 MiB, roughly a |
| @@ -680,15 +668,15 @@ | |
| 680 | --publish 127.0.0.1:9999:8080 \ |
| 681 | localhost/fossil |
| 682 | $ podman start fossil |
| 683 | ``` |
| 684 | |
| 685 | [pmmac]: https://podman.io/getting-started/installation.html#macos |
| 686 | [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md |
| 687 | [Podman]: https://podman.io/ |
| 688 | [rootless]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md |
| 689 | [whatis]: https://podman.io/whatis.html |
| 690 | |
| 691 | |
| 692 | ### 6.3 <a id="nspawn"></a>`systemd-container` |
| 693 | |
| 694 | If even the Podman stack is too big for you, the next-best option I’m |
| @@ -788,11 +776,11 @@ | |
| 788 | * The path in the host-side part of the `Bind` value must point at the |
| 789 | directory containing the `repo.fossil` file referenced in said |
| 790 | command so that `/museum/repo.fossil` refers to your repo out |
| 791 | on the host for the reasons given [above](#bind-mount). |
| 792 | |
| 793 | That being done, we also need a generic systemd unit file called |
| 794 | `/etc/systemd/system/[email protected]`, containing: |
| 795 | |
| 796 | ---- |
| 797 | |
| 798 | ``` |
| @@ -841,11 +829,11 @@ | |
| 841 | ``` |
| 842 | |
| 843 | You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability |
| 844 | to allow Fossil to bind to this low-numbered port. |
| 845 | |
| 846 | We use systemd’s template file feature to allow multiple Fossil |
| 847 | servers running on a single machine, each on a different TCP port, |
| 848 | as when proxying them out as subdirectories of a larger site. |
| 849 | To add another project, you must first clone the base “machine” layer: |
| 850 | |
| 851 | ``` |
| @@ -996,11 +984,11 @@ | |
| 996 | you’re serving Fossil from, you may need to know which assumptions |
| 997 | our container violates and the resulting consequences. |
| 998 | |
| 999 | Some of it we discussed above already, but there’s one big class of |
| 1000 | problems we haven’t covered yet. It stems from the fact that our stock |
| 1001 | container starts a single static executable inside a barebones container |
| 1002 | rather than “boot” an OS image. That causes a bunch of commands to fail: |
| 1003 | |
| 1004 | * **`machinectl poweroff`** will fail because the container |
| 1005 | isn’t running dbus. |
| 1006 | |
| 1007 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -69,11 +69,11 @@ | |
| 69 | The wrong way is to use the `Dockerfile COPY` command, because by baking |
| 70 | the repo into the image at build time, it will become one of the image’s |
| 71 | base layers. The end result is that each time you build a container from |
| 72 | that image, the repo will be reset to its build-time state. Worse, |
| 73 | restarting the container will do the same thing, since the base image |
| 74 | layers are immutable. This is almost certainly not what you |
| 75 | want. |
| 76 | |
| 77 | The correct ways put the repo into the _container_ created from the |
| 78 | _image_, not in the image itself. |
| 79 | |
| @@ -114,11 +114,11 @@ | |
| 114 | this with `chmod`: simply reload the browser, and Fossil will try again. |
| 115 | |
| 116 | |
| 117 | ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container |
| 118 | |
| 119 | The simple storage method above has a problem: containers are |
| 120 | designed to be killed off at the slightest cause, rebuilt, and |
| 121 | redeployed. If you do that with the repo inside the container, it gets |
| 122 | destroyed, too. The solution is to replace the “run” command above with |
| 123 | the following: |
| 124 | |
| @@ -133,12 +133,12 @@ | |
| 133 | Because this bind mount maps a host-side directory (`~/museum`) into the |
| 134 | container, you don’t need to `docker cp` the repo into the container at |
| 135 | all. It still expects to find the repository as `repo.fossil` under that |
| 136 | directory, but now both the host and the container can see that repo DB. |
| 137 | |
| 138 | Instead of a bind mount, you could instead set up a separate |
| 139 | [volume](https://docs.docker.com/storage/volumes/), at which point you |
| 140 | _would_ need to `docker cp` the repo file into the container. |
| 141 | |
| 142 | Either way, files in these mounted directories have a lifetime |
| 143 | independent of the container(s) they’re mounted into. When you need to |
| 144 | rebuild the container or its underlying image — such as to upgrade to a |
| @@ -183,152 +183,25 @@ | |
| 183 | |
| 184 | ## 3. <a id="security"></a>Security |
| 185 | |
| 186 | ### 3.1 <a id="chroot"></a>Why Not Chroot? |
| 187 | |
| 188 | Prior to 2023.03.26, the stock Fossil container relied on [the chroot |
| 189 | jail feature](./chroot.md) to wall away the shell and other tools |
| 190 | provided by [BusyBox]. It included that as a bare-bones operating system |
| 191 | inside the container on the off chance that someone might need it for |
| 192 | debugging, but the thing is, Fossil is self-contained, needing none of |
| 193 | that power in the main-line use cases. |
| 194 | |
| 195 | Our weak “you might need it” justification collapsed when we realized |
| 196 | you could restore this basic shell environment with a one-line change to |
| 197 | the `Dockerfile`, as shown [below](#run). |
| 198 | |
| 199 | [BusyBox]: https://www.busybox.net/BusyBox.html |
| 200 | |
| 201 | |
| 202 | ### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities |
| 203 | |
| 204 | The example commands above create the container with [a default set of |
| 205 | Linux kernel capabilities][defcap]. Although Docker strips away almost |
| 206 | all of the traditional root capabilities by default, and Fossil doesn’t |
| 207 | need any of those it does take away, Docker does leave some enabled that |
| @@ -443,12 +316,12 @@ | |
| 316 | |
| 317 | |
| 318 | ## 4. <a id="static"></a>Extracting a Static Binary |
| 319 | |
| 320 | Our 2-stage build process uses Alpine Linux only as a build host. Once |
| 321 | we’ve got everything reduced to a single static Fossil binary, |
| 322 | we throw all the rest of it away. |
| 323 | |
| 324 | A secondary benefit falls out of this process for free: it’s arguably |
| 325 | the easiest way to build a purely static Fossil binary for Linux. Most |
| 326 | modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s |
| 327 | back-to-basics nature makes static builds work the way they used to, |
| @@ -460,17 +333,17 @@ | |
| 333 | $ docker create --name fossil-static-tmp fossil |
| 334 | $ docker cp fossil-static-tmp:/bin/fossil . |
| 335 | $ docker container rm fossil-static-tmp |
| 336 | ``` |
| 337 | |
| 338 | The result is six or seven megs, depending on the CPU architecture you |
| 339 | build for. It’s built stripped. |
| 340 | |
| 341 | [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead |
| 342 | |
| 343 | |
| 344 | ## 5. <a id="custom" name="args"></a>Customization Points |
| 345 | |
| 346 | ### <a id="pkg-vers"></a> 5.1 Fossil Version |
| 347 | |
| 348 | The default version of Fossil fetched in the build is the version in the |
| 349 | checkout directory at the time you run it. You could override it to get |
| @@ -486,21 +359,21 @@ | |
| 359 | $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20' |
| 360 | ``` |
| 361 | |
| 362 | While you could instead use the generic |
| 363 | “`release`” tag here, it’s better to use a specific version number |
| 364 | since container builders cache downloaded files, hoping to |
| 365 | reuse them across builds. If you ask for “`release`” before a new |
| 366 | version is tagged and then immediately after, you might expect to get |
| 367 | two different tarballs, but because the underlying source tarball URL |
| 368 | remains the same when you do that, you’ll end up reusing the |
| 369 | old tarball from cache. This will occur |
| 370 | even if you pass the “`docker build --no-cache`” option. |
| 371 | |
| 372 | This is why we default to pulling the Fossil tarball by checkin ID |
| 373 | rather than let it default to the generic “`trunk`” tag: so the URL will |
| 374 | change each time you update your Fossil source tree, forcing the builder to |
| 375 | pull a fresh tarball. |
| 376 | |
| 377 | |
| 378 | ### 5.2 <a id="uids"></a>User & Group IDs |
| 379 | |
| @@ -512,22 +385,34 @@ | |
| 385 | close to zero as we can manage. |
| 386 | |
| 387 | To change it to something else, say: |
| 388 | |
| 389 | ``` |
| 390 | $ make container-image DBFLAGS='--build-arg UID=501' |
| 391 | ``` |
| 392 | |
| 393 | This is particularly useful if you’re putting your repository on a |
| 394 | separate volume since the IDs “leak” out into the host environment via |
| 395 | file permissions. You may therefore wish them to mean something on both |
| 396 | sides of the container barrier rather than have “499” appear on the host |
| 397 | in “`ls -l`” output. |
| 398 | |
| 399 | |
| 400 | ### 5.3 <a id="cengine"></a>Container Engine |
| 401 | |
| 402 | Although the Fossil container build system defaults to Docker, we allow |
| 403 | for use of any OCI container system that implements the same interfaces. |
| 404 | We go into more details about this [below](#light), but |
| 405 | for now, it suffices to point out that you can switch to Podman while |
| 406 | using our `Makefile` convenience targets unchanged by saying: |
| 407 | |
| 408 | ``` |
| 409 | $ make CENGINE=podman container-run |
| 410 | ``` |
| 411 | |
| 412 | |
| 413 | ### 5.4 <a id="config"></a>Fossil Configuration Options |
| 414 | |
| 415 | You can use this same mechanism to enable non-default Fossil |
| 416 | configuration options in your build. For instance, to turn on |
| 417 | the JSON API and the TH1 docs extension: |
| 418 | |
| @@ -535,31 +420,133 @@ | |
| 420 | $ make container-image \ |
| 421 | DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"' |
| 422 | ``` |
| 423 | |
| 424 | If you also wanted [the Tcl evaluation extension](./th1.md#tclEval), |
| 425 | that brings us to [the next point](#run). |
| 426 | |
| 427 | |
| 428 | ### 5.5 <a id="run"></a>Elaborating the Run Layer |
| 429 | |
| 430 | If you want a basic shell environment for temporary debugging of the |
| 431 | running container, that’s easily added. Simply change this line in the |
| 432 | `Dockerfile`… |
| 433 | |
| 434 | FROM scratch AS run |
| 435 | |
| 436 | …to this: |
| 437 | |
| 438 | FROM busybox AS run |
| 439 | |
| 440 | Rebuild and redeploy to give your Fossil container a [BusyBox]-based |
| 441 | shell environment that you can get into via: |
| 442 | |
| 443 | $ docker exec -it -u fossil $(make container-version) sh |
| 444 | |
| 445 | (That command assumes you built it via “`make container`” and are |
| 446 | therefore using its versioning scheme.) |
| 447 | |
| 448 | Another case where you might need to replace this bare-bones “`run`” |
| 449 | layer with something more functional is that you’re setting up [email |
| 450 | alerts](./alerts.md) and need some way to integrate with the host’s |
| 451 | [MTA]. There are a number of alternatives in that linked document, so |
| 452 | for the sake of discussion, we’ll say you’ve chosen [Method |
| 453 | 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite |
| 454 | extension to push messages into the outbound email queue DB, presumably |
| 455 | bind-mounted into the container. |
| 456 | |
| 457 | You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` |
| 458 | with this: |
| 459 | |
| 460 | ``` |
| 461 | ## --------------------------------------------------------------------- |
| 462 | ## STAGE 2: Pare that back to the bare essentials, plus Tcl. |
| 463 | ## --------------------------------------------------------------------- |
| 464 | FROM alpine AS run |
| 465 | ARG UID=499 |
| 466 | ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" |
| 467 | COPY --from=builder /tmp/fossil /bin/ |
| 468 | COPY tools/email-sender.tcl /bin/ |
| 469 | RUN set -x \ |
| 470 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ |
| 471 | && echo "fossil:x:${UID}:fossil" >> /etc/group \ |
| 472 | && install -d -m 700 -o fossil -g fossil log museum \ |
| 473 | && apk add --no-cache tcl sqlite-tcl |
| 474 | ``` |
| 475 | |
| 476 | Build it and test that it works like so: |
| 477 | |
| 478 | ``` |
| 479 | $ make container-run && |
| 480 | echo 'puts [info patchlevel]' | |
| 481 | docker exec -i $(make container-version) tclsh |
| 482 | 8.6.12 |
| 483 | ``` |
| 484 | |
| 485 | You should remove the `PATH` override in the “RUN” |
| 486 | stage, since it’s written for the case where everything is in `/bin`. |
| 487 | With these additions, we need the longer `PATH` shown above to have |
| 488 | ready access to them all. |
| 489 | |
| 490 | Another useful case to consider is that you’ve installed a [server |
| 491 | extension](./serverext.wiki) and you need an interpreter for that |
| 492 | script. The first option above won’t work except in the unlikely case that |
| 493 | it’s written for one of the bare-bones script interpreters that BusyBox |
| 494 | ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist |
| 495 | shell, implementing little more than what POSIX specified in 1989, plus |
| 496 | equally stripped-down versions of `awk` and `sed`.) |
| 497 | |
| 498 | Let’s say the extension is written in Python. While you could handle it |
| 499 | the same way we do with the Tcl example above, Python is more |
| 500 | popular, giving us more options. Let’s inject a Python environment into |
| 501 | the stock Fossil container via a suitable “[distroless]” image instead: |
| 502 | |
| 503 | ``` |
| 504 | ## --------------------------------------------------------------------- |
| 505 | ## STAGE 2: Pare that back to the bare essentials, plus Python. |
| 506 | ## --------------------------------------------------------------------- |
| 507 | FROM cgr.dev/chainguard/python:latest |
| 508 | USER root |
| 509 | ARG UID=499 |
| 510 | ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" |
| 511 | COPY --from=builder /tmp/fossil /bin/ |
| 512 | COPY --from=builder /bin/busybox.static /bin/busybox |
| 513 | RUN [ "/bin/busybox", "--install", "/bin" ] |
| 514 | RUN set -x \ |
| 515 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ |
| 516 | && echo "fossil:x:${UID}:fossil" >> /etc/group \ |
| 517 | && install -d -m 700 -o fossil -g fossil log museum |
| 518 | ``` |
| 519 | |
| 520 | You will also have to add `busybox-static` to the APK package list in |
| 521 | STAGE 1 for the `RUN` script at the end of that stage to work, since the |
| 522 | [Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to |
| 523 | install root-level binaries is why we change `USER` temporarily here. |
| 524 | |
| 525 | Build it and test that it works like so: |
| 526 | |
| 527 | ``` |
| 528 | $ make container-run && |
| 529 | docker exec -i $(make container-version) python --version |
| 530 | 3.11.2 |
| 531 | ``` |
| 532 | |
| 533 | The compensation for the hassle of using Chainguard over something more |
| 534 | general purpose like Alpine + “`apk add python`” |
| 535 | is huge: we no longer leave a package manager sitting around inside the |
| 536 | container, waiting for some malefactor to figure out how to abuse it. |
| 537 | |
| 538 | Beware that there’s a limit to this über-jail’s ability to save you when |
| 539 | you go and provide a more capable OS layer like this. The container |
| 540 | layer should stop an attacker from accessing any files out on the host |
| 541 | that you haven’t explicitly mounted into the container’s namespace, but |
| 542 | it can’t stop them from making outbound network connections or modifying |
| 543 | the repo DB inside the container. |
| 544 | |
| 545 | [cgimgs]: https://github.com/chainguard-images/images/tree/main/images |
| 546 | [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future |
| 547 | [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent |
| 548 | |
| 549 | |
| 550 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 551 | |
| 552 | Those afflicted with sticker shock at seeing the size of a [Docker |
| @@ -567,13 +554,13 @@ | |
| 554 | “noped” out of the whole concept of containers. The first thing to |
| 555 | realize is that when it comes to actually serving simple containers like |
| 556 | the ones shown above is that [Docker Engine][DE] suffices, at about a |
| 557 | quarter of the size. |
| 558 | |
| 559 | Yet on a small server — say, a $4/month ten gig Digital Ocean droplet — |
| 560 | that’s still a big chunk of your storage budget. It takes ~60:1 overhead |
| 561 | merely to run a Fossil server container? Once again, I wouldn’t |
| 562 | blame you if you noped right on out of here, but if you will be patient, |
| 563 | you will find that there are ways to run Fossil inside a container even |
| 564 | on entry-level cloud VPSes. These are well-suited to running Fossil; you |
| 565 | don’t have to resort to [raw Fossil service][srv] to succeed, |
| 566 | leaving the benefits of containerization to those with bigger budgets. |
| @@ -587,11 +574,11 @@ | |
| 574 | --publish 127.0.0.1:9999:8080 |
| 575 | ``` |
| 576 | |
| 577 | The assumption is that there’s a reverse proxy running somewhere that |
| 578 | redirects public web hits to localhost port 9999, which in turn goes to |
| 579 | port 8080 inside the container. This use of port |
| 580 | publishing effectively replaces the use of the |
| 581 | “`fossil server --localhost`” option. |
| 582 | |
| 583 | For the nginx case, you need to add `--scgi` to these commands, and you |
| 584 | might also need to specify `--baseurl`. |
| @@ -616,17 +603,18 @@ | |
| 603 | |
| 604 | |
| 605 | ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down |
| 606 | |
| 607 | The core of Docker Engine is its [`containerd`][ctrd] daemon and the |
| 608 | [`runc`][runc] container runtime. Add to this the out-of-core CLI program |
| 609 | [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil |
| 610 | containers. The big things you’re missing are: |
| 611 | |
| 612 | * **BuildKit**: The container build engine, which doesn’t matter if |
| 613 | you’re building elsewhere and shipping the images to the target. |
| 614 | A good example is using a container registry as an |
| 615 | intermediary between the build and deployment hosts. |
| 616 | |
| 617 | * **SwarmKit**: A powerful yet simple orchestrator for Docker that you |
| 618 | probably aren’t using with Fossil anyway. |
| 619 | |
| 620 | In exchange, you get a runtime that’s about half the size of Docker |
| @@ -640,12 +628,12 @@ | |
| 628 | [runc]: https://github.com/opencontainers/runc |
| 629 | |
| 630 | |
| 631 | ### 6.2 <a id="podman"></a>Podman |
| 632 | |
| 633 | A lighter-weight [rootless][rl] [drop-in replacement][whatis] that |
| 634 | doesn’t give up the image builder is [Podman]. Initially created by |
| 635 | Red Hat and thus popular on that family of OSes, it will run on |
| 636 | any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] |
| 637 | or [on Windows via WSL2][pmwin]. |
| 638 | |
| 639 | On Ubuntu 22.04, the installation size is about 38 MiB, roughly a |
| @@ -680,15 +668,15 @@ | |
| 668 | --publish 127.0.0.1:9999:8080 \ |
| 669 | localhost/fossil |
| 670 | $ podman start fossil |
| 671 | ``` |
| 672 | |
| 673 | [pmmac]: https://podman.io/getting-started/installation.html#macos |
| 674 | [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md |
| 675 | [Podman]: https://podman.io/ |
| 676 | [rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md |
| 677 | [whatis]: https://podman.io/whatis.html |
| 678 | |
| 679 | |
| 680 | ### 6.3 <a id="nspawn"></a>`systemd-container` |
| 681 | |
| 682 | If even the Podman stack is too big for you, the next-best option I’m |
| @@ -788,11 +776,11 @@ | |
| 776 | * The path in the host-side part of the `Bind` value must point at the |
| 777 | directory containing the `repo.fossil` file referenced in said |
| 778 | command so that `/museum/repo.fossil` refers to your repo out |
| 779 | on the host for the reasons given [above](#bind-mount). |
| 780 | |
| 781 | That being done, we also need a generic `systemd` unit file called |
| 782 | `/etc/systemd/system/[email protected]`, containing: |
| 783 | |
| 784 | ---- |
| 785 | |
| 786 | ``` |
| @@ -841,11 +829,11 @@ | |
| 829 | ``` |
| 830 | |
| 831 | You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability |
| 832 | to allow Fossil to bind to this low-numbered port. |
| 833 | |
| 834 | We use the `systemd` template file feature to allow multiple Fossil |
| 835 | servers running on a single machine, each on a different TCP port, |
| 836 | as when proxying them out as subdirectories of a larger site. |
| 837 | To add another project, you must first clone the base “machine” layer: |
| 838 | |
| 839 | ``` |
| @@ -996,11 +984,11 @@ | |
| 984 | you’re serving Fossil from, you may need to know which assumptions |
| 985 | our container violates and the resulting consequences. |
| 986 | |
| 987 | Some of it we discussed above already, but there’s one big class of |
| 988 | problems we haven’t covered yet. It stems from the fact that our stock |
| 989 | container starts a single static executable inside a bare-bones container |
| 990 | rather than “boot” an OS image. That causes a bunch of commands to fail: |
| 991 | |
| 992 | * **`machinectl poweroff`** will fail because the container |
| 993 | isn’t running dbus. |
| 994 | |
| 995 |
+22
-24
| --- www/custom_ticket.wiki | ||
| +++ www/custom_ticket.wiki | ||
| @@ -1,16 +1,16 @@ | ||
| 1 | 1 | <title>Customizing The Ticket System</title> |
| 2 | 2 | <nowiki> |
| 3 | 3 | <h2>Introduction</h2> |
| 4 | -<p> | |
| 4 | + | |
| 5 | 5 | This guide will explain how to add the "assigned_to" and "submitted_by" fields |
| 6 | 6 | to the ticket system in Fossil, as well as making the system more useful. You |
| 7 | 7 | must have "admin" access to the repository to implement these instructions. |
| 8 | -</p> | |
| 8 | + | |
| 9 | +<h2>First modify the TICKET table</h2> | |
| 9 | 10 | |
| 10 | -<h2>First modify the TICKET table</h2><blockquote> | |
| 11 | -<p> | |
| 11 | +<blockquote> | |
| 12 | 12 | Click on the "Admin" menu, then "Tickets", then "Table". After the other fields |
| 13 | 13 | and before the final ")", insert: |
| 14 | 14 | <pre> |
| 15 | 15 | , |
| 16 | 16 | assigned_to TEXT, |
| @@ -17,15 +17,15 @@ | ||
| 17 | 17 | opened_by TEXT |
| 18 | 18 | </pre> |
| 19 | 19 | And "Apply Changes". You have just added two more fields to the ticket |
| 20 | 20 | database! NOTE: I won't tell you to "Apply Changes" after each step from here |
| 21 | 21 | on out. Now, how do you use these fields? |
| 22 | -</p> | |
| 23 | 22 | </blockquote> |
| 24 | 23 | |
| 25 | -<h2>Next add assignees</h2><blockquote> | |
| 26 | -<p> | |
| 24 | +<h2>Next add assignees</h2> | |
| 25 | + | |
| 26 | +<blockquote> | |
| 27 | 27 | Back to the "Tickets" admin page, and click "Common". Add something like this: |
| 28 | 28 | <pre> |
| 29 | 29 | set assigned_choices { |
| 30 | 30 | unassigned |
| 31 | 31 | tom |
| @@ -34,15 +34,15 @@ | ||
| 34 | 34 | } |
| 35 | 35 | </pre> |
| 36 | 36 | Obviously, choose names corresponding to the logins on your system. The |
| 37 | 37 | 'unassigned' entry is important, as it prevents you from having a NULL in that |
| 38 | 38 | field (which causes problems later when editing). |
| 39 | -</p> | |
| 40 | 39 | </blockquote> |
| 41 | 40 | |
| 42 | -<h2>Now modify the 'new ticket' page</h2><blockquote> | |
| 43 | -<p> | |
| 41 | +<h2>Now modify the 'new ticket' page</h2> | |
| 42 | + | |
| 43 | +<blockquote> | |
| 44 | 44 | Back to the "Tickets" admin page, and click "New Ticket Page". This is a little |
| 45 | 45 | more tricky. Edit the top part: |
| 46 | 46 | <pre> |
| 47 | 47 | if {[info exists submit]} { |
| 48 | 48 | set status Open |
| @@ -66,22 +66,21 @@ | ||
| 66 | 66 | <th1>enable_output 1</th1> |
| 67 | 67 | </pre> |
| 68 | 68 | This bit of code will get rid of the "email" field entry for logged-in users. |
| 69 | 69 | Since we know the user's information, we don't have to ask for it. NOTE: it |
| 70 | 70 | might be good to automatically scoop up the user's email and put it here. |
| 71 | -</p> | |
| 72 | -<p> | |
| 71 | + | |
| 73 | 72 | You might also want to enable people to actually assign the ticket to a specific |
| 74 | 73 | person during creation. For this to work, you need to add the code |
| 75 | 74 | for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page". |
| 76 | 75 | This will give you an additional combobox where you can choose a person during |
| 77 | 76 | ticket creation. |
| 78 | -</p> | |
| 79 | 77 | </blockquote> |
| 80 | 78 | |
| 81 | -<h2>Modify the 'view ticket' page</h2><blockquote> | |
| 82 | -<p> | |
| 79 | +<h2>Modify the 'view ticket' page</h2> | |
| 80 | + | |
| 81 | +<blockquote> | |
| 83 | 82 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 84 | 83 | after the closing tr tag and before the "enable_output" line: |
| 85 | 84 | <pre> |
| 86 | 85 | <tr> |
| 87 | 86 | <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> |
| @@ -91,15 +90,15 @@ | ||
| 91 | 90 | $<opened_by> |
| 92 | 91 | </td> |
| 93 | 92 | </pre> |
| 94 | 93 | This will add a row which displays these two fields, in the event the user has |
| 95 | 94 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 96 | -</p> | |
| 97 | 95 | </blockquote> |
| 98 | 96 | |
| 99 | -<h2>Modify the 'edit ticket' page</h2><blockquote> | |
| 100 | -<p> | |
| 97 | +<h2>Modify the 'edit ticket' page</h2> | |
| 98 | + | |
| 99 | +<blockquote> | |
| 101 | 100 | Before the "Severity:" line, add this: |
| 102 | 101 | <pre> |
| 103 | 102 | <tr><td align="right">Assigned to:</td><td> |
| 104 | 103 | <th1>combobox assigned_to $assigned_choices 1</th1> |
| 105 | 104 | </td></tr> |
| @@ -107,25 +106,25 @@ | ||
| 107 | 106 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 108 | 107 | command 'combobox' is the database field which the combobox is associated to. |
| 109 | 108 | The next argument is the list of choices you want to show in the combobox (and |
| 110 | 109 | that you specified in the second step above. The last argument should be 1 for a |
| 111 | 110 | true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for |
| 112 | -details).</p> | |
| 111 | +details). | |
| 113 | 112 | |
| 114 | -<p>Now, similar to the previous | |
| 113 | +Now, similar to the previous | |
| 115 | 114 | section, look for "Contact:" and add this: |
| 116 | 115 | <pre> |
| 117 | 116 | <tr><td align="right">Reported by:</td><td> |
| 118 | 117 | <input type="text" name="opened_by" size="40" |
| 119 | 118 | value="$<opened_by>"> |
| 120 | 119 | </td></tr> |
| 121 | 120 | </pre> |
| 122 | -</p> | |
| 123 | 121 | </blockquote> |
| 124 | 122 | |
| 125 | -<h2>What next?</h2><blockquote> | |
| 126 | -<p> | |
| 123 | +<h2>What next?</h2> | |
| 124 | + | |
| 125 | +<blockquote> | |
| 127 | 126 | Now you can add custom reports which select based on the person to whom the |
| 128 | 127 | ticket is assigned. For example, an "Assigned to me" report could be: |
| 129 | 128 | <pre> |
| 130 | 129 | SELECT |
| 131 | 130 | CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc' |
| @@ -141,8 +140,7 @@ | ||
| 141 | 140 | subsystem, |
| 142 | 141 | title |
| 143 | 142 | FROM ticket |
| 144 | 143 | WHERE assigned_to=user() |
| 145 | 144 | </pre> |
| 146 | -</p> | |
| 147 | 145 | </blockquote> |
| 148 | 146 | </nowiki> |
| 149 | 147 |
| --- www/custom_ticket.wiki | |
| +++ www/custom_ticket.wiki | |
| @@ -1,16 +1,16 @@ | |
| 1 | <title>Customizing The Ticket System</title> |
| 2 | <nowiki> |
| 3 | <h2>Introduction</h2> |
| 4 | <p> |
| 5 | This guide will explain how to add the "assigned_to" and "submitted_by" fields |
| 6 | to the ticket system in Fossil, as well as making the system more useful. You |
| 7 | must have "admin" access to the repository to implement these instructions. |
| 8 | </p> |
| 9 | |
| 10 | <h2>First modify the TICKET table</h2><blockquote> |
| 11 | <p> |
| 12 | Click on the "Admin" menu, then "Tickets", then "Table". After the other fields |
| 13 | and before the final ")", insert: |
| 14 | <pre> |
| 15 | , |
| 16 | assigned_to TEXT, |
| @@ -17,15 +17,15 @@ | |
| 17 | opened_by TEXT |
| 18 | </pre> |
| 19 | And "Apply Changes". You have just added two more fields to the ticket |
| 20 | database! NOTE: I won't tell you to "Apply Changes" after each step from here |
| 21 | on out. Now, how do you use these fields? |
| 22 | </p> |
| 23 | </blockquote> |
| 24 | |
| 25 | <h2>Next add assignees</h2><blockquote> |
| 26 | <p> |
| 27 | Back to the "Tickets" admin page, and click "Common". Add something like this: |
| 28 | <pre> |
| 29 | set assigned_choices { |
| 30 | unassigned |
| 31 | tom |
| @@ -34,15 +34,15 @@ | |
| 34 | } |
| 35 | </pre> |
| 36 | Obviously, choose names corresponding to the logins on your system. The |
| 37 | 'unassigned' entry is important, as it prevents you from having a NULL in that |
| 38 | field (which causes problems later when editing). |
| 39 | </p> |
| 40 | </blockquote> |
| 41 | |
| 42 | <h2>Now modify the 'new ticket' page</h2><blockquote> |
| 43 | <p> |
| 44 | Back to the "Tickets" admin page, and click "New Ticket Page". This is a little |
| 45 | more tricky. Edit the top part: |
| 46 | <pre> |
| 47 | if {[info exists submit]} { |
| 48 | set status Open |
| @@ -66,22 +66,21 @@ | |
| 66 | <th1>enable_output 1</th1> |
| 67 | </pre> |
| 68 | This bit of code will get rid of the "email" field entry for logged-in users. |
| 69 | Since we know the user's information, we don't have to ask for it. NOTE: it |
| 70 | might be good to automatically scoop up the user's email and put it here. |
| 71 | </p> |
| 72 | <p> |
| 73 | You might also want to enable people to actually assign the ticket to a specific |
| 74 | person during creation. For this to work, you need to add the code |
| 75 | for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page". |
| 76 | This will give you an additional combobox where you can choose a person during |
| 77 | ticket creation. |
| 78 | </p> |
| 79 | </blockquote> |
| 80 | |
| 81 | <h2>Modify the 'view ticket' page</h2><blockquote> |
| 82 | <p> |
| 83 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 84 | after the closing tr tag and before the "enable_output" line: |
| 85 | <pre> |
| 86 | <tr> |
| 87 | <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> |
| @@ -91,15 +90,15 @@ | |
| 91 | $<opened_by> |
| 92 | </td> |
| 93 | </pre> |
| 94 | This will add a row which displays these two fields, in the event the user has |
| 95 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 96 | </p> |
| 97 | </blockquote> |
| 98 | |
| 99 | <h2>Modify the 'edit ticket' page</h2><blockquote> |
| 100 | <p> |
| 101 | Before the "Severity:" line, add this: |
| 102 | <pre> |
| 103 | <tr><td align="right">Assigned to:</td><td> |
| 104 | <th1>combobox assigned_to $assigned_choices 1</th1> |
| 105 | </td></tr> |
| @@ -107,25 +106,25 @@ | |
| 107 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 108 | command 'combobox' is the database field which the combobox is associated to. |
| 109 | The next argument is the list of choices you want to show in the combobox (and |
| 110 | that you specified in the second step above. The last argument should be 1 for a |
| 111 | true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for |
| 112 | details).</p> |
| 113 | |
| 114 | <p>Now, similar to the previous |
| 115 | section, look for "Contact:" and add this: |
| 116 | <pre> |
| 117 | <tr><td align="right">Reported by:</td><td> |
| 118 | <input type="text" name="opened_by" size="40" |
| 119 | value="$<opened_by>"> |
| 120 | </td></tr> |
| 121 | </pre> |
| 122 | </p> |
| 123 | </blockquote> |
| 124 | |
| 125 | <h2>What next?</h2><blockquote> |
| 126 | <p> |
| 127 | Now you can add custom reports which select based on the person to whom the |
| 128 | ticket is assigned. For example, an "Assigned to me" report could be: |
| 129 | <pre> |
| 130 | SELECT |
| 131 | CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc' |
| @@ -141,8 +140,7 @@ | |
| 141 | subsystem, |
| 142 | title |
| 143 | FROM ticket |
| 144 | WHERE assigned_to=user() |
| 145 | </pre> |
| 146 | </p> |
| 147 | </blockquote> |
| 148 | </nowiki> |
| 149 |
| --- www/custom_ticket.wiki | |
| +++ www/custom_ticket.wiki | |
| @@ -1,16 +1,16 @@ | |
| 1 | <title>Customizing The Ticket System</title> |
| 2 | <nowiki> |
| 3 | <h2>Introduction</h2> |
| 4 | |
| 5 | This guide will explain how to add the "assigned_to" and "submitted_by" fields |
| 6 | to the ticket system in Fossil, as well as making the system more useful. You |
| 7 | must have "admin" access to the repository to implement these instructions. |
| 8 | |
| 9 | <h2>First modify the TICKET table</h2> |
| 10 | |
| 11 | <blockquote> |
| 12 | Click on the "Admin" menu, then "Tickets", then "Table". After the other fields |
| 13 | and before the final ")", insert: |
| 14 | <pre> |
| 15 | , |
| 16 | assigned_to TEXT, |
| @@ -17,15 +17,15 @@ | |
| 17 | opened_by TEXT |
| 18 | </pre> |
| 19 | And "Apply Changes". You have just added two more fields to the ticket |
| 20 | database! NOTE: I won't tell you to "Apply Changes" after each step from here |
| 21 | on out. Now, how do you use these fields? |
| 22 | </blockquote> |
| 23 | |
| 24 | <h2>Next add assignees</h2> |
| 25 | |
| 26 | <blockquote> |
| 27 | Back to the "Tickets" admin page, and click "Common". Add something like this: |
| 28 | <pre> |
| 29 | set assigned_choices { |
| 30 | unassigned |
| 31 | tom |
| @@ -34,15 +34,15 @@ | |
| 34 | } |
| 35 | </pre> |
| 36 | Obviously, choose names corresponding to the logins on your system. The |
| 37 | 'unassigned' entry is important, as it prevents you from having a NULL in that |
| 38 | field (which causes problems later when editing). |
| 39 | </blockquote> |
| 40 | |
| 41 | <h2>Now modify the 'new ticket' page</h2> |
| 42 | |
| 43 | <blockquote> |
| 44 | Back to the "Tickets" admin page, and click "New Ticket Page". This is a little |
| 45 | more tricky. Edit the top part: |
| 46 | <pre> |
| 47 | if {[info exists submit]} { |
| 48 | set status Open |
| @@ -66,22 +66,21 @@ | |
| 66 | <th1>enable_output 1</th1> |
| 67 | </pre> |
| 68 | This bit of code will get rid of the "email" field entry for logged-in users. |
| 69 | Since we know the user's information, we don't have to ask for it. NOTE: it |
| 70 | might be good to automatically scoop up the user's email and put it here. |
| 71 | |
| 72 | You might also want to enable people to actually assign the ticket to a specific |
| 73 | person during creation. For this to work, you need to add the code |
| 74 | for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page". |
| 75 | This will give you an additional combobox where you can choose a person during |
| 76 | ticket creation. |
| 77 | </blockquote> |
| 78 | |
| 79 | <h2>Modify the 'view ticket' page</h2> |
| 80 | |
| 81 | <blockquote> |
| 82 | Look for the text "Contact:" (about halfway through). Then insert these lines |
| 83 | after the closing tr tag and before the "enable_output" line: |
| 84 | <pre> |
| 85 | <tr> |
| 86 | <td align="right">Assigned to:</td><td bgcolor="#d0d0d0"> |
| @@ -91,15 +90,15 @@ | |
| 90 | $<opened_by> |
| 91 | </td> |
| 92 | </pre> |
| 93 | This will add a row which displays these two fields, in the event the user has |
| 94 | <a href="./caps/ref.html#w">ticket "edit" capability</a>. |
| 95 | </blockquote> |
| 96 | |
| 97 | <h2>Modify the 'edit ticket' page</h2> |
| 98 | |
| 99 | <blockquote> |
| 100 | Before the "Severity:" line, add this: |
| 101 | <pre> |
| 102 | <tr><td align="right">Assigned to:</td><td> |
| 103 | <th1>combobox assigned_to $assigned_choices 1</th1> |
| 104 | </td></tr> |
| @@ -107,25 +106,25 @@ | |
| 106 | That will give you a drop-down list of assignees. The first argument to the TH1 |
| 107 | command 'combobox' is the database field which the combobox is associated to. |
| 108 | The next argument is the list of choices you want to show in the combobox (and |
| 109 | that you specified in the second step above. The last argument should be 1 for a |
| 110 | true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for |
| 111 | details). |
| 112 | |
| 113 | Now, similar to the previous |
| 114 | section, look for "Contact:" and add this: |
| 115 | <pre> |
| 116 | <tr><td align="right">Reported by:</td><td> |
| 117 | <input type="text" name="opened_by" size="40" |
| 118 | value="$<opened_by>"> |
| 119 | </td></tr> |
| 120 | </pre> |
| 121 | </blockquote> |
| 122 | |
| 123 | <h2>What next?</h2> |
| 124 | |
| 125 | <blockquote> |
| 126 | Now you can add custom reports which select based on the person to whom the |
| 127 | ticket is assigned. For example, an "Assigned to me" report could be: |
| 128 | <pre> |
| 129 | SELECT |
| 130 | CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc' |
| @@ -141,8 +140,7 @@ | |
| 140 | subsystem, |
| 141 | title |
| 142 | FROM ticket |
| 143 | WHERE assigned_to=user() |
| 144 | </pre> |
| 145 | </blockquote> |
| 146 | </nowiki> |
| 147 |
+14
-14
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -78,15 +78,15 @@ | ||
| 78 | 78 | generates the initial HTML Header section of a page. The |
| 79 | 79 | generated HTML Header will look something like this: |
| 80 | 80 | |
| 81 | 81 | <html> |
| 82 | 82 | <head> |
| 83 | - <base href="..." /> | |
| 84 | - <meta http-equiv="Content-Security-Policy" content="...." /> | |
| 83 | + <base href="..."> | |
| 84 | + <meta http-equiv="Content-Security-Policy" content="...."> | |
| 85 | 85 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 86 | 86 | <title>....</title> |
| 87 | - <link rel="stylesheet" href="..." type="text/css" /> | |
| 87 | + <link rel="stylesheet" href="..." type="text/css"> | |
| 88 | 88 | </head> |
| 89 | 89 | <body class="FEATURE"> |
| 90 | 90 | |
| 91 | 91 | …where `FEATURE` is either the top-level URL element (e.g. `doc`) or a |
| 92 | 92 | feature class that groups multiple URLs under a single name such as |
| @@ -180,21 +180,21 @@ | ||
| 180 | 180 | desired result. |
| 181 | 181 | |
| 182 | 182 | The skin is controlled by five files: |
| 183 | 183 | |
| 184 | 184 | <blockquote><dl> |
| 185 | -<dt><b>css.txt</b></dt><dd> | |
| 185 | +<dt><b>css.txt</b></dt> | |
| 186 | 186 | |
| 187 | -<p>The css.txt file is the text of the CSS for Fossil. | |
| 187 | +<dd>The css.txt file is the text of the CSS for Fossil. | |
| 188 | 188 | Fossil might add additional CSS elements after |
| 189 | 189 | the css.txt file, if it sees that the css.txt omits some |
| 190 | 190 | CSS components that Fossil needs. But for the most part, |
| 191 | 191 | the content of the css.txt is the CSS for the page.</dd> |
| 192 | 192 | |
| 193 | -<dt><b>details.txt</b><dt><dd> | |
| 193 | +<dt><b>details.txt</b><dt> | |
| 194 | 194 | |
| 195 | -<p>The details.txt file is short list of settings that control | |
| 195 | +<dd>The details.txt file is short list of settings that control | |
| 196 | 196 | the look and feel, mostly of the timeline. The default |
| 197 | 197 | details.txt file looks like this: |
| 198 | 198 | |
| 199 | 199 | <blockquote><pre> |
| 200 | 200 | pikchr-background: "" |
| @@ -211,11 +211,11 @@ | ||
| 211 | 211 | of certain aspects of the timeline graph. The number on the |
| 212 | 212 | right is a boolean - "1" to activate the feature and "0" to |
| 213 | 213 | disable it. The "white-foreground:" setting should be set to |
| 214 | 214 | "1" if the page color has light-color text on a darker background, |
| 215 | 215 | and "0" if the page has dark text on a light-colored background. |
| 216 | -<p> | |
| 216 | + | |
| 217 | 217 | If the "pikchr-foreground" setting (added in Fossil 2.14) |
| 218 | 218 | is defined and is not an empty string then it specifies a |
| 219 | 219 | foreground color to use for [pikchr diagrams](./pikchr.md). The |
| 220 | 220 | default pikchr foreground color is black, or white if the |
| 221 | 221 | "white-foreground" boolean is set. The "pikchr-background" |
| @@ -224,24 +224,24 @@ | ||
| 224 | 224 | empty strings, then they should be floating point values (close |
| 225 | 225 | to 1.0) that specify relative scaling of the fonts in pikchr |
| 226 | 226 | diagrams and other elements of the diagrams, respectively. |
| 227 | 227 | </dd> |
| 228 | 228 | |
| 229 | -<dt><b>footer.txt</b> and <b>header.txt</b></dt><dd> | |
| 229 | +<dt><b>footer.txt</b> and <b>header.txt</b></dt> | |
| 230 | 230 | |
| 231 | -<p>The footer.txt and header.txt files contain the Content Footer | |
| 231 | +<dd>The footer.txt and header.txt files contain the Content Footer | |
| 232 | 232 | and Content Header respectively. Of these, the Content Header is |
| 233 | 233 | the most important, as it contains the markup used to generate |
| 234 | 234 | the banner and menu bar for each page. |
| 235 | 235 | |
| 236 | -<p>Both the footer.txt and header.txt file are | |
| 236 | +Both the footer.txt and header.txt file are | |
| 237 | 237 | [processed using TH1](#headfoot) prior to being output as |
| 238 | 238 | part of the overall web page.</dd> |
| 239 | 239 | |
| 240 | -<dt><b>js.txt</b></dt><dd> | |
| 240 | +<dt><b>js.txt</b></dt> | |
| 241 | 241 | |
| 242 | -<p>The js.txt file is optional. It is intended to be javascript. | |
| 242 | +<dd>The js.txt file is optional. It is intended to be javascript. | |
| 243 | 243 | The complete text of this javascript might be inserted into |
| 244 | 244 | the Content Footer, after being processed using TH1, using |
| 245 | 245 | code like the following in the "footer.txt" file: |
| 246 | 246 | |
| 247 | 247 | <blockquote><pre> |
| @@ -248,11 +248,11 @@ | ||
| 248 | 248 | <script nonce="$nonce"> |
| 249 | 249 | <th1>styleScript</th1> |
| 250 | 250 | </script> |
| 251 | 251 | </pre></blockquote> |
| 252 | 252 | |
| 253 | -<p>The js.txt file was originally used to insert javascript | |
| 253 | +The js.txt file was originally used to insert javascript | |
| 254 | 254 | that controls the hamburger menu in the default skin. More |
| 255 | 255 | recently, the javascript for the hamburger menu was moved into |
| 256 | 256 | a separate built-in file. Skins that use the hamburger menu |
| 257 | 257 | typically cause the javascript to be loaded by including the |
| 258 | 258 | following TH1 code in the "header.txt" file: |
| 259 | 259 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -78,15 +78,15 @@ | |
| 78 | generates the initial HTML Header section of a page. The |
| 79 | generated HTML Header will look something like this: |
| 80 | |
| 81 | <html> |
| 82 | <head> |
| 83 | <base href="..." /> |
| 84 | <meta http-equiv="Content-Security-Policy" content="...." /> |
| 85 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 86 | <title>....</title> |
| 87 | <link rel="stylesheet" href="..." type="text/css" /> |
| 88 | </head> |
| 89 | <body class="FEATURE"> |
| 90 | |
| 91 | …where `FEATURE` is either the top-level URL element (e.g. `doc`) or a |
| 92 | feature class that groups multiple URLs under a single name such as |
| @@ -180,21 +180,21 @@ | |
| 180 | desired result. |
| 181 | |
| 182 | The skin is controlled by five files: |
| 183 | |
| 184 | <blockquote><dl> |
| 185 | <dt><b>css.txt</b></dt><dd> |
| 186 | |
| 187 | <p>The css.txt file is the text of the CSS for Fossil. |
| 188 | Fossil might add additional CSS elements after |
| 189 | the css.txt file, if it sees that the css.txt omits some |
| 190 | CSS components that Fossil needs. But for the most part, |
| 191 | the content of the css.txt is the CSS for the page.</dd> |
| 192 | |
| 193 | <dt><b>details.txt</b><dt><dd> |
| 194 | |
| 195 | <p>The details.txt file is short list of settings that control |
| 196 | the look and feel, mostly of the timeline. The default |
| 197 | details.txt file looks like this: |
| 198 | |
| 199 | <blockquote><pre> |
| 200 | pikchr-background: "" |
| @@ -211,11 +211,11 @@ | |
| 211 | of certain aspects of the timeline graph. The number on the |
| 212 | right is a boolean - "1" to activate the feature and "0" to |
| 213 | disable it. The "white-foreground:" setting should be set to |
| 214 | "1" if the page color has light-color text on a darker background, |
| 215 | and "0" if the page has dark text on a light-colored background. |
| 216 | <p> |
| 217 | If the "pikchr-foreground" setting (added in Fossil 2.14) |
| 218 | is defined and is not an empty string then it specifies a |
| 219 | foreground color to use for [pikchr diagrams](./pikchr.md). The |
| 220 | default pikchr foreground color is black, or white if the |
| 221 | "white-foreground" boolean is set. The "pikchr-background" |
| @@ -224,24 +224,24 @@ | |
| 224 | empty strings, then they should be floating point values (close |
| 225 | to 1.0) that specify relative scaling of the fonts in pikchr |
| 226 | diagrams and other elements of the diagrams, respectively. |
| 227 | </dd> |
| 228 | |
| 229 | <dt><b>footer.txt</b> and <b>header.txt</b></dt><dd> |
| 230 | |
| 231 | <p>The footer.txt and header.txt files contain the Content Footer |
| 232 | and Content Header respectively. Of these, the Content Header is |
| 233 | the most important, as it contains the markup used to generate |
| 234 | the banner and menu bar for each page. |
| 235 | |
| 236 | <p>Both the footer.txt and header.txt file are |
| 237 | [processed using TH1](#headfoot) prior to being output as |
| 238 | part of the overall web page.</dd> |
| 239 | |
| 240 | <dt><b>js.txt</b></dt><dd> |
| 241 | |
| 242 | <p>The js.txt file is optional. It is intended to be javascript. |
| 243 | The complete text of this javascript might be inserted into |
| 244 | the Content Footer, after being processed using TH1, using |
| 245 | code like the following in the "footer.txt" file: |
| 246 | |
| 247 | <blockquote><pre> |
| @@ -248,11 +248,11 @@ | |
| 248 | <script nonce="$nonce"> |
| 249 | <th1>styleScript</th1> |
| 250 | </script> |
| 251 | </pre></blockquote> |
| 252 | |
| 253 | <p>The js.txt file was originally used to insert javascript |
| 254 | that controls the hamburger menu in the default skin. More |
| 255 | recently, the javascript for the hamburger menu was moved into |
| 256 | a separate built-in file. Skins that use the hamburger menu |
| 257 | typically cause the javascript to be loaded by including the |
| 258 | following TH1 code in the "header.txt" file: |
| 259 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -78,15 +78,15 @@ | |
| 78 | generates the initial HTML Header section of a page. The |
| 79 | generated HTML Header will look something like this: |
| 80 | |
| 81 | <html> |
| 82 | <head> |
| 83 | <base href="..."> |
| 84 | <meta http-equiv="Content-Security-Policy" content="...."> |
| 85 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 86 | <title>....</title> |
| 87 | <link rel="stylesheet" href="..." type="text/css"> |
| 88 | </head> |
| 89 | <body class="FEATURE"> |
| 90 | |
| 91 | …where `FEATURE` is either the top-level URL element (e.g. `doc`) or a |
| 92 | feature class that groups multiple URLs under a single name such as |
| @@ -180,21 +180,21 @@ | |
| 180 | desired result. |
| 181 | |
| 182 | The skin is controlled by five files: |
| 183 | |
| 184 | <blockquote><dl> |
| 185 | <dt><b>css.txt</b></dt> |
| 186 | |
| 187 | <dd>The css.txt file is the text of the CSS for Fossil. |
| 188 | Fossil might add additional CSS elements after |
| 189 | the css.txt file, if it sees that the css.txt omits some |
| 190 | CSS components that Fossil needs. But for the most part, |
| 191 | the content of the css.txt is the CSS for the page.</dd> |
| 192 | |
| 193 | <dt><b>details.txt</b><dt> |
| 194 | |
| 195 | <dd>The details.txt file is short list of settings that control |
| 196 | the look and feel, mostly of the timeline. The default |
| 197 | details.txt file looks like this: |
| 198 | |
| 199 | <blockquote><pre> |
| 200 | pikchr-background: "" |
| @@ -211,11 +211,11 @@ | |
| 211 | of certain aspects of the timeline graph. The number on the |
| 212 | right is a boolean - "1" to activate the feature and "0" to |
| 213 | disable it. The "white-foreground:" setting should be set to |
| 214 | "1" if the page color has light-color text on a darker background, |
| 215 | and "0" if the page has dark text on a light-colored background. |
| 216 | |
| 217 | If the "pikchr-foreground" setting (added in Fossil 2.14) |
| 218 | is defined and is not an empty string then it specifies a |
| 219 | foreground color to use for [pikchr diagrams](./pikchr.md). The |
| 220 | default pikchr foreground color is black, or white if the |
| 221 | "white-foreground" boolean is set. The "pikchr-background" |
| @@ -224,24 +224,24 @@ | |
| 224 | empty strings, then they should be floating point values (close |
| 225 | to 1.0) that specify relative scaling of the fonts in pikchr |
| 226 | diagrams and other elements of the diagrams, respectively. |
| 227 | </dd> |
| 228 | |
| 229 | <dt><b>footer.txt</b> and <b>header.txt</b></dt> |
| 230 | |
| 231 | <dd>The footer.txt and header.txt files contain the Content Footer |
| 232 | and Content Header respectively. Of these, the Content Header is |
| 233 | the most important, as it contains the markup used to generate |
| 234 | the banner and menu bar for each page. |
| 235 | |
| 236 | Both the footer.txt and header.txt file are |
| 237 | [processed using TH1](#headfoot) prior to being output as |
| 238 | part of the overall web page.</dd> |
| 239 | |
| 240 | <dt><b>js.txt</b></dt> |
| 241 | |
| 242 | <dd>The js.txt file is optional. It is intended to be javascript. |
| 243 | The complete text of this javascript might be inserted into |
| 244 | the Content Footer, after being processed using TH1, using |
| 245 | code like the following in the "footer.txt" file: |
| 246 | |
| 247 | <blockquote><pre> |
| @@ -248,11 +248,11 @@ | |
| 248 | <script nonce="$nonce"> |
| 249 | <th1>styleScript</th1> |
| 250 | </script> |
| 251 | </pre></blockquote> |
| 252 | |
| 253 | The js.txt file was originally used to insert javascript |
| 254 | that controls the hamburger menu in the default skin. More |
| 255 | recently, the javascript for the hamburger menu was moved into |
| 256 | a separate built-in file. Skins that use the hamburger menu |
| 257 | typically cause the javascript to be loaded by including the |
| 258 | following TH1 code in the "header.txt" file: |
| 259 |
+1
-1
| --- www/delta_encoder_algorithm.wiki | ||
| +++ www/delta_encoder_algorithm.wiki | ||
| @@ -83,11 +83,11 @@ | ||
| 83 | 83 | <li>A hash table is filled, mapping from the hashes of the chunks to |
| 84 | 84 | the list of chunk locations having this hash. |
| 85 | 85 | </li> |
| 86 | 86 | </ol> |
| 87 | 87 | |
| 88 | -<h3 id="processing">2.1 Processing the target</h3> | |
| 88 | +<h3 id="processing">2.2 Processing the target</h3> | |
| 89 | 89 | |
| 90 | 90 | <p>This, the main phase of the encoder, processes the target in a loop |
| 91 | 91 | from beginning to end. The state of the encoder is captured by two |
| 92 | 92 | locations, the "base" and the "slide". "base" points to the first byte |
| 93 | 93 | of the target for which no delta output has been generated yet, and |
| 94 | 94 |
| --- www/delta_encoder_algorithm.wiki | |
| +++ www/delta_encoder_algorithm.wiki | |
| @@ -83,11 +83,11 @@ | |
| 83 | <li>A hash table is filled, mapping from the hashes of the chunks to |
| 84 | the list of chunk locations having this hash. |
| 85 | </li> |
| 86 | </ol> |
| 87 | |
| 88 | <h3 id="processing">2.1 Processing the target</h3> |
| 89 | |
| 90 | <p>This, the main phase of the encoder, processes the target in a loop |
| 91 | from beginning to end. The state of the encoder is captured by two |
| 92 | locations, the "base" and the "slide". "base" points to the first byte |
| 93 | of the target for which no delta output has been generated yet, and |
| 94 |
| --- www/delta_encoder_algorithm.wiki | |
| +++ www/delta_encoder_algorithm.wiki | |
| @@ -83,11 +83,11 @@ | |
| 83 | <li>A hash table is filled, mapping from the hashes of the chunks to |
| 84 | the list of chunk locations having this hash. |
| 85 | </li> |
| 86 | </ol> |
| 87 | |
| 88 | <h3 id="processing">2.2 Processing the target</h3> |
| 89 | |
| 90 | <p>This, the main phase of the encoder, processes the target in a loop |
| 91 | from beginning to end. The state of the encoder is captured by two |
| 92 | locations, the "base" and the "slide". "base" points to the first byte |
| 93 | of the target for which no delta output has been generated yet, and |
| 94 |
+38
-44
| --- www/delta_format.wiki | ||
| +++ www/delta_format.wiki | ||
| @@ -1,34 +1,33 @@ | ||
| 1 | 1 | <title>Fossil Delta Format</title> |
| 2 | 2 | <h1>1.0 Overview</h1> |
| 3 | 3 | |
| 4 | -<p>Fossil achieves efficient storage and low-bandwidth synchronization | |
| 4 | +Fossil achieves efficient storage and low-bandwidth synchronization | |
| 5 | 5 | through the use of delta-compression. Instead of storing |
| 6 | 6 | or transmitting the complete content of an artifact, Fossil stores or |
| 7 | 7 | transmits only the changes relative to a related artifact. |
| 8 | -</p> | |
| 9 | 8 | |
| 10 | -<p>This document describes the delta-encoding format used by Fossil. | |
| 9 | +This document describes the delta-encoding format used by Fossil. | |
| 11 | 10 | The intended audience is developers working on either |
| 12 | 11 | <a href="index.wiki">Fossil</a> itself, or on tools compatible with |
| 13 | 12 | Fossil. Understanding of this document is <em>not</em> required for |
| 14 | -ordinary users of Fossil. This document is an implementation detail.</p> | |
| 13 | +ordinary users of Fossil. This document is an implementation detail. | |
| 15 | 14 | |
| 16 | -<p>This document only describes the delta file format. A | |
| 15 | +This document only describes the delta file format. A | |
| 17 | 16 | [./delta_encoder_algorithm.wiki|separate document] describes one possible |
| 18 | -algorithm for generating deltas in this format.</p> | |
| 17 | +algorithm for generating deltas in this format. | |
| 19 | 18 | |
| 20 | 19 | <h2>1.1 Sample Software And Analysis Tools</h2> |
| 21 | 20 | |
| 22 | -<p>The core routines used to generate and apply deltas in this format | |
| 21 | +The core routines used to generate and apply deltas in this format | |
| 23 | 22 | are contained in the [../src/delta.c|delta.c] source file. Interface |
| 24 | 23 | logic, including "test-*" commands to directly manipulate deltas are |
| 25 | 24 | contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions |
| 26 | 25 | to create, apply, and analyze deltas are implemented by code in the |
| 27 | 26 | [../src/deltafunc.c|deltafunc.c] source file. |
| 28 | 27 | |
| 29 | -<p>The following command-line tools are available to create and apply | |
| 28 | +The following command-line tools are available to create and apply | |
| 30 | 29 | deltas and to test the delta logic: |
| 31 | 30 | |
| 32 | 31 | * [/help?cmd=test-delta|fossil test-delta] → Run self-tests of |
| 33 | 32 | the delta logic |
| 34 | 33 | |
| @@ -40,11 +39,11 @@ | ||
| 40 | 39 | |
| 41 | 40 | * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute |
| 42 | 41 | and delta that converts file X into file Y but instead of writing the |
| 43 | 42 | delta to output, write performance information about the delta. |
| 44 | 43 | |
| 45 | -<p>When running the [/help?cmd=sqlite3|fossil sql] command to get an | |
| 44 | +When running the [/help?cmd=sqlite3|fossil sql] command to get an | |
| 46 | 45 | interactive SQL session connected to the repository, the following |
| 47 | 46 | additional SQL functions are provided: |
| 48 | 47 | |
| 49 | 48 | * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> → |
| 50 | 49 | Compute a data that carries blob X into blob Y and return that delta |
| @@ -68,53 +67,53 @@ | ||
| 68 | 67 | box height 50% "Header" |
| 69 | 68 | box same "Segments" |
| 70 | 69 | box same "Trailer" |
| 71 | 70 | </verbatim> |
| 72 | 71 | |
| 73 | -<p>A delta consists of three parts, a "header", a "trailer", and a | |
| 74 | -"segment-list" between them.</p> | |
| 72 | +A delta consists of three parts, a "header", a "trailer", and a | |
| 73 | +"segment-list" between them. | |
| 75 | 74 | |
| 76 | -<p>Both header and trailer provide information about the target | |
| 75 | +Both header and trailer provide information about the target | |
| 77 | 76 | helping the decoder, and the segment-list describes how the target can |
| 78 | -be constructed from the original.</p> | |
| 77 | +be constructed from the original. | |
| 79 | 78 | |
| 80 | 79 | <h2 id="header">2.1 Header</h2> |
| 81 | 80 | <verbatim type="pikchr"> |
| 82 | 81 | leftmargin = 0.1 |
| 83 | 82 | box height 50% "Size" |
| 84 | 83 | box same "\"\\n\"" |
| 85 | 84 | </verbatim> |
| 86 | 85 | |
| 87 | -<p>The header consists of a single number followed by a newline | |
| 86 | +The header consists of a single number followed by a newline | |
| 88 | 87 | character (ASCII 0x0a). The number is the length of the target in |
| 89 | -bytes.</p> | |
| 88 | +bytes. | |
| 90 | 89 | |
| 91 | -<p>This means that, given a delta, the decoder can compute the size of | |
| 90 | +This means that, given a delta, the decoder can compute the size of | |
| 92 | 91 | the target (and allocate any necessary memory based on that) by simply |
| 93 | 92 | reading the first line of the delta and decoding the number found |
| 94 | -there. In other words, before it has to decode everything else.</p> | |
| 93 | +there. In other words, before it has to decode everything else. | |
| 95 | 94 | |
| 96 | 95 | <h2 id="trailer">2.2 Trailer</h2> |
| 97 | 96 | <verbatim type="pikchr"> |
| 98 | 97 | leftmargin = 0.1 |
| 99 | 98 | box height 50% "Checksum" |
| 100 | 99 | box same "\";\"" |
| 101 | 100 | </verbatim> |
| 102 | 101 | |
| 103 | -<p>The trailer consists of a single number followed by a semicolon (ASCII | |
| 102 | +The trailer consists of a single number followed by a semicolon (ASCII | |
| 104 | 103 | 0x3b). This number is a checksum of the target and can be used by a |
| 105 | 104 | decoder to verify that the delta applied correctly, reconstructing the |
| 106 | -target from the original.</p> | |
| 105 | +target from the original. | |
| 107 | 106 | |
| 108 | -<p>The checksum is computed by treating the target as a series of | |
| 107 | +The checksum is computed by treating the target as a series of | |
| 109 | 108 | 32-bit integer numbers (MSB first), and summing these up, modulo |
| 110 | 109 | 2^32-1. A target whose length is not a multiple of 4 is padded with |
| 111 | -0-bytes (ASCII 0x00) at the end.</p> | |
| 110 | +0-bytes (ASCII 0x00) at the end. | |
| 112 | 111 | |
| 113 | -<p>By putting this information at the end of the delta a decoder has | |
| 112 | +By putting this information at the end of the delta a decoder has | |
| 114 | 113 | it available immediately after the target has been reconstructed |
| 115 | -fully.</p> | |
| 114 | +fully. | |
| 116 | 115 | |
| 117 | 116 | <h2 id="slist">2.3 Segment-List</h2> |
| 118 | 117 | <verbatim type="pikchr"> |
| 119 | 118 | leftmargin = 0.1 |
| 120 | 119 | PART1: [ |
| @@ -138,41 +137,41 @@ | ||
| 138 | 137 | box "\":\"" same |
| 139 | 138 | box "Bytes" same |
| 140 | 139 | ] with .nw at previous.sw |
| 141 | 140 | </verbatim> |
| 142 | 141 | |
| 143 | -<p>The segment-list of a delta describes how to create the target from | |
| 142 | +The segment-list of a delta describes how to create the target from | |
| 144 | 143 | the original by a combination of inserting literal byte-sequences and |
| 145 | 144 | copying ranges of bytes from the original. This is where the |
| 146 | 145 | compression takes place, by encoding the large common parts of |
| 147 | -original and target in small copy instructions.</p> | |
| 146 | +original and target in small copy instructions. | |
| 148 | 147 | |
| 149 | -<p>The target is constructed from beginning to end, with the data | |
| 148 | +The target is constructed from beginning to end, with the data | |
| 150 | 149 | generated by each instruction appended after the data of all previous |
| 151 | -instructions, with no gaps.</p> | |
| 150 | +instructions, with no gaps. | |
| 152 | 151 | |
| 153 | 152 | <h3 id="insertlit">2.3.1 Insert Literal</h3> |
| 154 | 153 | |
| 155 | -<p>A literal is specified by two elements, the size of the literal in | |
| 156 | -bytes, and the bytes of the literal itself.</p> | |
| 154 | +A literal is specified by two elements, the size of the literal in | |
| 155 | +bytes, and the bytes of the literal itself. | |
| 157 | 156 | |
| 158 | 157 | <verbatim type="pikchr"> |
| 159 | 158 | leftmargin = 0.1 |
| 160 | 159 | box "Length" height 50% |
| 161 | 160 | box "\":\"" same |
| 162 | 161 | box "Bytes" same |
| 163 | 162 | </verbatim> |
| 164 | 163 | |
| 165 | -<p>The length is written first, followed by a colon character (ASCII | |
| 166 | -0x3a), followed by the bytes of the literal.</p> | |
| 164 | +The length is written first, followed by a colon character (ASCII | |
| 165 | +0x3a), followed by the bytes of the literal. | |
| 167 | 166 | |
| 168 | 167 | <h3 id="copyrange">2.3.2 Copy Range</h3> |
| 169 | 168 | |
| 170 | -<p>A range to copy is specified by two numbers, the offset of the | |
| 169 | +A range to copy is specified by two numbers, the offset of the | |
| 171 | 170 | first byte in the original to copy, and the size of the range, in |
| 172 | 171 | bytes. The size zero is special, its usage indicates that the range |
| 173 | -extends to the end of the original.</p> | |
| 172 | +extends to the end of the original. | |
| 174 | 173 | |
| 175 | 174 | <verbatim type="pikchr"> |
| 176 | 175 | leftmargin = 0.1 |
| 177 | 176 | box "Length" height 50% |
| 178 | 177 | box "\"@\"" same |
| @@ -179,37 +178,32 @@ | ||
| 179 | 178 | box "Offset" same |
| 180 | 179 | box "\",\"" same |
| 181 | 180 | </verbatim> |
| 182 | 181 | |
| 183 | 182 | |
| 184 | -<p>The length is written first, followed by an "at" character (ASCII | |
| 185 | -0x40), then the offset, followed by a comma (ASCII 0x2c).</p> | |
| 183 | +The length is written first, followed by an "at" character (ASCII | |
| 184 | +0x40), then the offset, followed by a comma (ASCII 0x2c). | |
| 186 | 185 | |
| 187 | 186 | <h1 id="intcoding">3.0 Encoding of integers</h1> |
| 188 | 187 | |
| 189 | -<p> | |
| 190 | 188 | The format currently handles only 32 bit integer numbers. They are |
| 191 | 189 | written base-64 encoded, MSB first, and without leading |
| 192 | 190 | "0"-characters, except if they are significant (i.e. 0 => "0"). |
| 193 | -</p> | |
| 194 | 191 | |
| 195 | -<p> | |
| 196 | 192 | The base-64 encoding uses one character for each 6 bits of |
| 197 | 193 | the integer to be encoded. The encoding characters are: |
| 198 | -</p> | |
| 199 | 194 | |
| 200 | 195 | <blockquote><pre> |
| 201 | 196 | 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ |
| 202 | 197 | </pre></blockquote> |
| 203 | 198 | |
| 204 | -<p>The least significant 6 bits of the integer are encoded by | |
| 199 | +The least significant 6 bits of the integer are encoded by | |
| 205 | 200 | the first character, followed by |
| 206 | 201 | the next 6 bits, and so on until all non-zero bits of the integer |
| 207 | 202 | are encoded. The minimum number of encoding characters is used. |
| 208 | 203 | Note that for integers less than 10, the base-64 coding is a |
| 209 | 204 | ASCII decimal rendering of the number itself. |
| 210 | -</p> | |
| 211 | 205 | |
| 212 | 206 | <h1 id="examples">4.0 Examples</h1> |
| 213 | 207 | |
| 214 | 208 | <h2 id="examplesint">4.1 Integer encoding</h2> |
| 215 | 209 | |
| @@ -232,18 +226,18 @@ | ||
| 232 | 226 | </tr> |
| 233 | 227 | </table> |
| 234 | 228 | |
| 235 | 229 | <h2 id="examplesdelta">4.2 Delta encoding</h2> |
| 236 | 230 | |
| 237 | -<p>An example of a delta using the specified encoding is:</p> | |
| 231 | +An example of a delta using the specified encoding is: | |
| 238 | 232 | |
| 239 | 233 | <table border=1><tr><td><pre> |
| 240 | 234 | 1Xb |
| 241 | 235 | 4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre> |
| 242 | 236 | </td></tr></table> |
| 243 | 237 | |
| 244 | -<p>This can be taken apart into the following parts:</p> | |
| 238 | +This can be taken apart into the following parts: | |
| 245 | 239 | |
| 246 | 240 | <table border=1> |
| 247 | 241 | <tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr> |
| 248 | 242 | <tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr> |
| 249 | 243 | <tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr> |
| @@ -258,11 +252,11 @@ | ||
| 258 | 252 | <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> |
| 259 | 253 | <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> |
| 260 | 254 | <tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr> |
| 261 | 255 | </table> |
| 262 | 256 | |
| 263 | -<p>The unified diff behind the above delta is</p> | |
| 257 | +The unified diff behind the above delta is | |
| 264 | 258 | |
| 265 | 259 | <table border=1><tr><td><pre> |
| 266 | 260 | bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new |
| 267 | 261 | --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 |
| 268 | 262 | +++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700 |
| 269 | 263 |
| --- www/delta_format.wiki | |
| +++ www/delta_format.wiki | |
| @@ -1,34 +1,33 @@ | |
| 1 | <title>Fossil Delta Format</title> |
| 2 | <h1>1.0 Overview</h1> |
| 3 | |
| 4 | <p>Fossil achieves efficient storage and low-bandwidth synchronization |
| 5 | through the use of delta-compression. Instead of storing |
| 6 | or transmitting the complete content of an artifact, Fossil stores or |
| 7 | transmits only the changes relative to a related artifact. |
| 8 | </p> |
| 9 | |
| 10 | <p>This document describes the delta-encoding format used by Fossil. |
| 11 | The intended audience is developers working on either |
| 12 | <a href="index.wiki">Fossil</a> itself, or on tools compatible with |
| 13 | Fossil. Understanding of this document is <em>not</em> required for |
| 14 | ordinary users of Fossil. This document is an implementation detail.</p> |
| 15 | |
| 16 | <p>This document only describes the delta file format. A |
| 17 | [./delta_encoder_algorithm.wiki|separate document] describes one possible |
| 18 | algorithm for generating deltas in this format.</p> |
| 19 | |
| 20 | <h2>1.1 Sample Software And Analysis Tools</h2> |
| 21 | |
| 22 | <p>The core routines used to generate and apply deltas in this format |
| 23 | are contained in the [../src/delta.c|delta.c] source file. Interface |
| 24 | logic, including "test-*" commands to directly manipulate deltas are |
| 25 | contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions |
| 26 | to create, apply, and analyze deltas are implemented by code in the |
| 27 | [../src/deltafunc.c|deltafunc.c] source file. |
| 28 | |
| 29 | <p>The following command-line tools are available to create and apply |
| 30 | deltas and to test the delta logic: |
| 31 | |
| 32 | * [/help?cmd=test-delta|fossil test-delta] → Run self-tests of |
| 33 | the delta logic |
| 34 | |
| @@ -40,11 +39,11 @@ | |
| 40 | |
| 41 | * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute |
| 42 | and delta that converts file X into file Y but instead of writing the |
| 43 | delta to output, write performance information about the delta. |
| 44 | |
| 45 | <p>When running the [/help?cmd=sqlite3|fossil sql] command to get an |
| 46 | interactive SQL session connected to the repository, the following |
| 47 | additional SQL functions are provided: |
| 48 | |
| 49 | * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> → |
| 50 | Compute a data that carries blob X into blob Y and return that delta |
| @@ -68,53 +67,53 @@ | |
| 68 | box height 50% "Header" |
| 69 | box same "Segments" |
| 70 | box same "Trailer" |
| 71 | </verbatim> |
| 72 | |
| 73 | <p>A delta consists of three parts, a "header", a "trailer", and a |
| 74 | "segment-list" between them.</p> |
| 75 | |
| 76 | <p>Both header and trailer provide information about the target |
| 77 | helping the decoder, and the segment-list describes how the target can |
| 78 | be constructed from the original.</p> |
| 79 | |
| 80 | <h2 id="header">2.1 Header</h2> |
| 81 | <verbatim type="pikchr"> |
| 82 | leftmargin = 0.1 |
| 83 | box height 50% "Size" |
| 84 | box same "\"\\n\"" |
| 85 | </verbatim> |
| 86 | |
| 87 | <p>The header consists of a single number followed by a newline |
| 88 | character (ASCII 0x0a). The number is the length of the target in |
| 89 | bytes.</p> |
| 90 | |
| 91 | <p>This means that, given a delta, the decoder can compute the size of |
| 92 | the target (and allocate any necessary memory based on that) by simply |
| 93 | reading the first line of the delta and decoding the number found |
| 94 | there. In other words, before it has to decode everything else.</p> |
| 95 | |
| 96 | <h2 id="trailer">2.2 Trailer</h2> |
| 97 | <verbatim type="pikchr"> |
| 98 | leftmargin = 0.1 |
| 99 | box height 50% "Checksum" |
| 100 | box same "\";\"" |
| 101 | </verbatim> |
| 102 | |
| 103 | <p>The trailer consists of a single number followed by a semicolon (ASCII |
| 104 | 0x3b). This number is a checksum of the target and can be used by a |
| 105 | decoder to verify that the delta applied correctly, reconstructing the |
| 106 | target from the original.</p> |
| 107 | |
| 108 | <p>The checksum is computed by treating the target as a series of |
| 109 | 32-bit integer numbers (MSB first), and summing these up, modulo |
| 110 | 2^32-1. A target whose length is not a multiple of 4 is padded with |
| 111 | 0-bytes (ASCII 0x00) at the end.</p> |
| 112 | |
| 113 | <p>By putting this information at the end of the delta a decoder has |
| 114 | it available immediately after the target has been reconstructed |
| 115 | fully.</p> |
| 116 | |
| 117 | <h2 id="slist">2.3 Segment-List</h2> |
| 118 | <verbatim type="pikchr"> |
| 119 | leftmargin = 0.1 |
| 120 | PART1: [ |
| @@ -138,41 +137,41 @@ | |
| 138 | box "\":\"" same |
| 139 | box "Bytes" same |
| 140 | ] with .nw at previous.sw |
| 141 | </verbatim> |
| 142 | |
| 143 | <p>The segment-list of a delta describes how to create the target from |
| 144 | the original by a combination of inserting literal byte-sequences and |
| 145 | copying ranges of bytes from the original. This is where the |
| 146 | compression takes place, by encoding the large common parts of |
| 147 | original and target in small copy instructions.</p> |
| 148 | |
| 149 | <p>The target is constructed from beginning to end, with the data |
| 150 | generated by each instruction appended after the data of all previous |
| 151 | instructions, with no gaps.</p> |
| 152 | |
| 153 | <h3 id="insertlit">2.3.1 Insert Literal</h3> |
| 154 | |
| 155 | <p>A literal is specified by two elements, the size of the literal in |
| 156 | bytes, and the bytes of the literal itself.</p> |
| 157 | |
| 158 | <verbatim type="pikchr"> |
| 159 | leftmargin = 0.1 |
| 160 | box "Length" height 50% |
| 161 | box "\":\"" same |
| 162 | box "Bytes" same |
| 163 | </verbatim> |
| 164 | |
| 165 | <p>The length is written first, followed by a colon character (ASCII |
| 166 | 0x3a), followed by the bytes of the literal.</p> |
| 167 | |
| 168 | <h3 id="copyrange">2.3.2 Copy Range</h3> |
| 169 | |
| 170 | <p>A range to copy is specified by two numbers, the offset of the |
| 171 | first byte in the original to copy, and the size of the range, in |
| 172 | bytes. The size zero is special, its usage indicates that the range |
| 173 | extends to the end of the original.</p> |
| 174 | |
| 175 | <verbatim type="pikchr"> |
| 176 | leftmargin = 0.1 |
| 177 | box "Length" height 50% |
| 178 | box "\"@\"" same |
| @@ -179,37 +178,32 @@ | |
| 179 | box "Offset" same |
| 180 | box "\",\"" same |
| 181 | </verbatim> |
| 182 | |
| 183 | |
| 184 | <p>The length is written first, followed by an "at" character (ASCII |
| 185 | 0x40), then the offset, followed by a comma (ASCII 0x2c).</p> |
| 186 | |
| 187 | <h1 id="intcoding">3.0 Encoding of integers</h1> |
| 188 | |
| 189 | <p> |
| 190 | The format currently handles only 32 bit integer numbers. They are |
| 191 | written base-64 encoded, MSB first, and without leading |
| 192 | "0"-characters, except if they are significant (i.e. 0 => "0"). |
| 193 | </p> |
| 194 | |
| 195 | <p> |
| 196 | The base-64 encoding uses one character for each 6 bits of |
| 197 | the integer to be encoded. The encoding characters are: |
| 198 | </p> |
| 199 | |
| 200 | <blockquote><pre> |
| 201 | 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ |
| 202 | </pre></blockquote> |
| 203 | |
| 204 | <p>The least significant 6 bits of the integer are encoded by |
| 205 | the first character, followed by |
| 206 | the next 6 bits, and so on until all non-zero bits of the integer |
| 207 | are encoded. The minimum number of encoding characters is used. |
| 208 | Note that for integers less than 10, the base-64 coding is a |
| 209 | ASCII decimal rendering of the number itself. |
| 210 | </p> |
| 211 | |
| 212 | <h1 id="examples">4.0 Examples</h1> |
| 213 | |
| 214 | <h2 id="examplesint">4.1 Integer encoding</h2> |
| 215 | |
| @@ -232,18 +226,18 @@ | |
| 232 | </tr> |
| 233 | </table> |
| 234 | |
| 235 | <h2 id="examplesdelta">4.2 Delta encoding</h2> |
| 236 | |
| 237 | <p>An example of a delta using the specified encoding is:</p> |
| 238 | |
| 239 | <table border=1><tr><td><pre> |
| 240 | 1Xb |
| 241 | 4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre> |
| 242 | </td></tr></table> |
| 243 | |
| 244 | <p>This can be taken apart into the following parts:</p> |
| 245 | |
| 246 | <table border=1> |
| 247 | <tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr> |
| 248 | <tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr> |
| 249 | <tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr> |
| @@ -258,11 +252,11 @@ | |
| 258 | <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> |
| 259 | <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> |
| 260 | <tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr> |
| 261 | </table> |
| 262 | |
| 263 | <p>The unified diff behind the above delta is</p> |
| 264 | |
| 265 | <table border=1><tr><td><pre> |
| 266 | bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new |
| 267 | --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 |
| 268 | +++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700 |
| 269 |
| --- www/delta_format.wiki | |
| +++ www/delta_format.wiki | |
| @@ -1,34 +1,33 @@ | |
| 1 | <title>Fossil Delta Format</title> |
| 2 | <h1>1.0 Overview</h1> |
| 3 | |
| 4 | Fossil achieves efficient storage and low-bandwidth synchronization |
| 5 | through the use of delta-compression. Instead of storing |
| 6 | or transmitting the complete content of an artifact, Fossil stores or |
| 7 | transmits only the changes relative to a related artifact. |
| 8 | |
| 9 | This document describes the delta-encoding format used by Fossil. |
| 10 | The intended audience is developers working on either |
| 11 | <a href="index.wiki">Fossil</a> itself, or on tools compatible with |
| 12 | Fossil. Understanding of this document is <em>not</em> required for |
| 13 | ordinary users of Fossil. This document is an implementation detail. |
| 14 | |
| 15 | This document only describes the delta file format. A |
| 16 | [./delta_encoder_algorithm.wiki|separate document] describes one possible |
| 17 | algorithm for generating deltas in this format. |
| 18 | |
| 19 | <h2>1.1 Sample Software And Analysis Tools</h2> |
| 20 | |
| 21 | The core routines used to generate and apply deltas in this format |
| 22 | are contained in the [../src/delta.c|delta.c] source file. Interface |
| 23 | logic, including "test-*" commands to directly manipulate deltas are |
| 24 | contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions |
| 25 | to create, apply, and analyze deltas are implemented by code in the |
| 26 | [../src/deltafunc.c|deltafunc.c] source file. |
| 27 | |
| 28 | The following command-line tools are available to create and apply |
| 29 | deltas and to test the delta logic: |
| 30 | |
| 31 | * [/help?cmd=test-delta|fossil test-delta] → Run self-tests of |
| 32 | the delta logic |
| 33 | |
| @@ -40,11 +39,11 @@ | |
| 39 | |
| 40 | * [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute |
| 41 | and delta that converts file X into file Y but instead of writing the |
| 42 | delta to output, write performance information about the delta. |
| 43 | |
| 44 | When running the [/help?cmd=sqlite3|fossil sql] command to get an |
| 45 | interactive SQL session connected to the repository, the following |
| 46 | additional SQL functions are provided: |
| 47 | |
| 48 | * <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> → |
| 49 | Compute a data that carries blob X into blob Y and return that delta |
| @@ -68,53 +67,53 @@ | |
| 67 | box height 50% "Header" |
| 68 | box same "Segments" |
| 69 | box same "Trailer" |
| 70 | </verbatim> |
| 71 | |
| 72 | A delta consists of three parts, a "header", a "trailer", and a |
| 73 | "segment-list" between them. |
| 74 | |
| 75 | Both header and trailer provide information about the target |
| 76 | helping the decoder, and the segment-list describes how the target can |
| 77 | be constructed from the original. |
| 78 | |
| 79 | <h2 id="header">2.1 Header</h2> |
| 80 | <verbatim type="pikchr"> |
| 81 | leftmargin = 0.1 |
| 82 | box height 50% "Size" |
| 83 | box same "\"\\n\"" |
| 84 | </verbatim> |
| 85 | |
| 86 | The header consists of a single number followed by a newline |
| 87 | character (ASCII 0x0a). The number is the length of the target in |
| 88 | bytes. |
| 89 | |
| 90 | This means that, given a delta, the decoder can compute the size of |
| 91 | the target (and allocate any necessary memory based on that) by simply |
| 92 | reading the first line of the delta and decoding the number found |
| 93 | there. In other words, before it has to decode everything else. |
| 94 | |
| 95 | <h2 id="trailer">2.2 Trailer</h2> |
| 96 | <verbatim type="pikchr"> |
| 97 | leftmargin = 0.1 |
| 98 | box height 50% "Checksum" |
| 99 | box same "\";\"" |
| 100 | </verbatim> |
| 101 | |
| 102 | The trailer consists of a single number followed by a semicolon (ASCII |
| 103 | 0x3b). This number is a checksum of the target and can be used by a |
| 104 | decoder to verify that the delta applied correctly, reconstructing the |
| 105 | target from the original. |
| 106 | |
| 107 | The checksum is computed by treating the target as a series of |
| 108 | 32-bit integer numbers (MSB first), and summing these up, modulo |
| 109 | 2^32-1. A target whose length is not a multiple of 4 is padded with |
| 110 | 0-bytes (ASCII 0x00) at the end. |
| 111 | |
| 112 | By putting this information at the end of the delta a decoder has |
| 113 | it available immediately after the target has been reconstructed |
| 114 | fully. |
| 115 | |
| 116 | <h2 id="slist">2.3 Segment-List</h2> |
| 117 | <verbatim type="pikchr"> |
| 118 | leftmargin = 0.1 |
| 119 | PART1: [ |
| @@ -138,41 +137,41 @@ | |
| 137 | box "\":\"" same |
| 138 | box "Bytes" same |
| 139 | ] with .nw at previous.sw |
| 140 | </verbatim> |
| 141 | |
| 142 | The segment-list of a delta describes how to create the target from |
| 143 | the original by a combination of inserting literal byte-sequences and |
| 144 | copying ranges of bytes from the original. This is where the |
| 145 | compression takes place, by encoding the large common parts of |
| 146 | original and target in small copy instructions. |
| 147 | |
| 148 | The target is constructed from beginning to end, with the data |
| 149 | generated by each instruction appended after the data of all previous |
| 150 | instructions, with no gaps. |
| 151 | |
| 152 | <h3 id="insertlit">2.3.1 Insert Literal</h3> |
| 153 | |
| 154 | A literal is specified by two elements, the size of the literal in |
| 155 | bytes, and the bytes of the literal itself. |
| 156 | |
| 157 | <verbatim type="pikchr"> |
| 158 | leftmargin = 0.1 |
| 159 | box "Length" height 50% |
| 160 | box "\":\"" same |
| 161 | box "Bytes" same |
| 162 | </verbatim> |
| 163 | |
| 164 | The length is written first, followed by a colon character (ASCII |
| 165 | 0x3a), followed by the bytes of the literal. |
| 166 | |
| 167 | <h3 id="copyrange">2.3.2 Copy Range</h3> |
| 168 | |
| 169 | A range to copy is specified by two numbers, the offset of the |
| 170 | first byte in the original to copy, and the size of the range, in |
| 171 | bytes. The size zero is special, its usage indicates that the range |
| 172 | extends to the end of the original. |
| 173 | |
| 174 | <verbatim type="pikchr"> |
| 175 | leftmargin = 0.1 |
| 176 | box "Length" height 50% |
| 177 | box "\"@\"" same |
| @@ -179,37 +178,32 @@ | |
| 178 | box "Offset" same |
| 179 | box "\",\"" same |
| 180 | </verbatim> |
| 181 | |
| 182 | |
| 183 | The length is written first, followed by an "at" character (ASCII |
| 184 | 0x40), then the offset, followed by a comma (ASCII 0x2c). |
| 185 | |
| 186 | <h1 id="intcoding">3.0 Encoding of integers</h1> |
| 187 | |
| 188 | The format currently handles only 32 bit integer numbers. They are |
| 189 | written base-64 encoded, MSB first, and without leading |
| 190 | "0"-characters, except if they are significant (i.e. 0 => "0"). |
| 191 | |
| 192 | The base-64 encoding uses one character for each 6 bits of |
| 193 | the integer to be encoded. The encoding characters are: |
| 194 | |
| 195 | <blockquote><pre> |
| 196 | 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ |
| 197 | </pre></blockquote> |
| 198 | |
| 199 | The least significant 6 bits of the integer are encoded by |
| 200 | the first character, followed by |
| 201 | the next 6 bits, and so on until all non-zero bits of the integer |
| 202 | are encoded. The minimum number of encoding characters is used. |
| 203 | Note that for integers less than 10, the base-64 coding is a |
| 204 | ASCII decimal rendering of the number itself. |
| 205 | |
| 206 | <h1 id="examples">4.0 Examples</h1> |
| 207 | |
| 208 | <h2 id="examplesint">4.1 Integer encoding</h2> |
| 209 | |
| @@ -232,18 +226,18 @@ | |
| 226 | </tr> |
| 227 | </table> |
| 228 | |
| 229 | <h2 id="examplesdelta">4.2 Delta encoding</h2> |
| 230 | |
| 231 | An example of a delta using the specified encoding is: |
| 232 | |
| 233 | <table border=1><tr><td><pre> |
| 234 | 1Xb |
| 235 | 4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre> |
| 236 | </td></tr></table> |
| 237 | |
| 238 | This can be taken apart into the following parts: |
| 239 | |
| 240 | <table border=1> |
| 241 | <tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr> |
| 242 | <tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr> |
| 243 | <tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr> |
| @@ -258,11 +252,11 @@ | |
| 252 | <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> |
| 253 | <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> |
| 254 | <tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr> |
| 255 | </table> |
| 256 | |
| 257 | The unified diff behind the above delta is |
| 258 | |
| 259 | <table border=1><tr><td><pre> |
| 260 | bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new |
| 261 | --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 |
| 262 | +++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700 |
| 263 |
+6
-5
| --- www/encryptedrepos.wiki | ||
| +++ www/encryptedrepos.wiki | ||
| @@ -5,41 +5,42 @@ | ||
| 5 | 5 | This technical note explains the process. |
| 6 | 6 | </blockquote> |
| 7 | 7 | <h2>Building An Encryption-Enabled Fossil</h2><blockquote> |
| 8 | 8 | The SQLite Encryption Extension (SEE) is proprietary software and requires |
| 9 | 9 | [https://sqlite.org/purchase/see|purchasing a license]. |
| 10 | -<p> | |
| 10 | + | |
| 11 | 11 | Assuming you have an SEE license, the first step of compiling Fossil to |
| 12 | 12 | use SEE is to create an SEE-enabled version of the SQLite database source code. |
| 13 | 13 | This alternative SQLite database source file should be called "sqlite3-see.c" |
| 14 | 14 | and should be placed in the extsrc/ subfolder of the Fossil sources, right beside |
| 15 | 15 | the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled |
| 16 | 16 | "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder |
| 17 | 17 | beside the original "shell.c". |
| 18 | -<p> | |
| 18 | + | |
| 19 | 19 | Add the --with-see command-line option to the configuration script to enable |
| 20 | 20 | the use of SEE on unix-like systems. |
| 21 | 21 | <blockquote><pre> |
| 22 | 22 | ./configure --with-see; make |
| 23 | 23 | </pre></blockquote> |
| 24 | -<p>To build for Windows using MSVC, add | |
| 24 | + | |
| 25 | +To build for Windows using MSVC, add | |
| 25 | 26 | the "USE_SEE=1" argument to the "nmake" command line. |
| 26 | 27 | <blockquote><pre> |
| 27 | 28 | nmake -f makefile.msc USE_SEE=1 |
| 28 | 29 | </pre></blockquote> |
| 29 | 30 | </blockquote> |
| 30 | 31 | <h2>Using Encrypted Repositories</h2><blockquote> |
| 31 | 32 | Any Fossil repositories whose filename ends with ".efossil" is taken to be |
| 32 | 33 | an encrypted repository. Fossil will prompt for the encryption password and |
| 33 | 34 | attempt to open the repository database using that password. |
| 34 | -<p> | |
| 35 | + | |
| 35 | 36 | Every invocation of fossil on an encrypted repository requires retyping the |
| 36 | 37 | encryption password. |
| 37 | 38 | To avoid excess password typing, consider using the "fossil shell" |
| 38 | 39 | command which prompts for the password just once, then reuses it for each |
| 39 | 40 | subsequent Fossil command entered at the prompt. |
| 40 | -<p> | |
| 41 | + | |
| 41 | 42 | On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not |
| 42 | 43 | (currently) work on an encrypted repository. |
| 43 | 44 | </blockquote> |
| 44 | 45 | <h2>Additional Security</h2><blockquote> |
| 45 | 46 | Use the FOSSIL_SECURITY_LEVEL environment for additional protection. |
| 46 | 47 |
| --- www/encryptedrepos.wiki | |
| +++ www/encryptedrepos.wiki | |
| @@ -5,41 +5,42 @@ | |
| 5 | This technical note explains the process. |
| 6 | </blockquote> |
| 7 | <h2>Building An Encryption-Enabled Fossil</h2><blockquote> |
| 8 | The SQLite Encryption Extension (SEE) is proprietary software and requires |
| 9 | [https://sqlite.org/purchase/see|purchasing a license]. |
| 10 | <p> |
| 11 | Assuming you have an SEE license, the first step of compiling Fossil to |
| 12 | use SEE is to create an SEE-enabled version of the SQLite database source code. |
| 13 | This alternative SQLite database source file should be called "sqlite3-see.c" |
| 14 | and should be placed in the extsrc/ subfolder of the Fossil sources, right beside |
| 15 | the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled |
| 16 | "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder |
| 17 | beside the original "shell.c". |
| 18 | <p> |
| 19 | Add the --with-see command-line option to the configuration script to enable |
| 20 | the use of SEE on unix-like systems. |
| 21 | <blockquote><pre> |
| 22 | ./configure --with-see; make |
| 23 | </pre></blockquote> |
| 24 | <p>To build for Windows using MSVC, add |
| 25 | the "USE_SEE=1" argument to the "nmake" command line. |
| 26 | <blockquote><pre> |
| 27 | nmake -f makefile.msc USE_SEE=1 |
| 28 | </pre></blockquote> |
| 29 | </blockquote> |
| 30 | <h2>Using Encrypted Repositories</h2><blockquote> |
| 31 | Any Fossil repositories whose filename ends with ".efossil" is taken to be |
| 32 | an encrypted repository. Fossil will prompt for the encryption password and |
| 33 | attempt to open the repository database using that password. |
| 34 | <p> |
| 35 | Every invocation of fossil on an encrypted repository requires retyping the |
| 36 | encryption password. |
| 37 | To avoid excess password typing, consider using the "fossil shell" |
| 38 | command which prompts for the password just once, then reuses it for each |
| 39 | subsequent Fossil command entered at the prompt. |
| 40 | <p> |
| 41 | On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not |
| 42 | (currently) work on an encrypted repository. |
| 43 | </blockquote> |
| 44 | <h2>Additional Security</h2><blockquote> |
| 45 | Use the FOSSIL_SECURITY_LEVEL environment for additional protection. |
| 46 |
| --- www/encryptedrepos.wiki | |
| +++ www/encryptedrepos.wiki | |
| @@ -5,41 +5,42 @@ | |
| 5 | This technical note explains the process. |
| 6 | </blockquote> |
| 7 | <h2>Building An Encryption-Enabled Fossil</h2><blockquote> |
| 8 | The SQLite Encryption Extension (SEE) is proprietary software and requires |
| 9 | [https://sqlite.org/purchase/see|purchasing a license]. |
| 10 | |
| 11 | Assuming you have an SEE license, the first step of compiling Fossil to |
| 12 | use SEE is to create an SEE-enabled version of the SQLite database source code. |
| 13 | This alternative SQLite database source file should be called "sqlite3-see.c" |
| 14 | and should be placed in the extsrc/ subfolder of the Fossil sources, right beside |
| 15 | the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled |
| 16 | "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder |
| 17 | beside the original "shell.c". |
| 18 | |
| 19 | Add the --with-see command-line option to the configuration script to enable |
| 20 | the use of SEE on unix-like systems. |
| 21 | <blockquote><pre> |
| 22 | ./configure --with-see; make |
| 23 | </pre></blockquote> |
| 24 | |
| 25 | To build for Windows using MSVC, add |
| 26 | the "USE_SEE=1" argument to the "nmake" command line. |
| 27 | <blockquote><pre> |
| 28 | nmake -f makefile.msc USE_SEE=1 |
| 29 | </pre></blockquote> |
| 30 | </blockquote> |
| 31 | <h2>Using Encrypted Repositories</h2><blockquote> |
| 32 | Any Fossil repositories whose filename ends with ".efossil" is taken to be |
| 33 | an encrypted repository. Fossil will prompt for the encryption password and |
| 34 | attempt to open the repository database using that password. |
| 35 | |
| 36 | Every invocation of fossil on an encrypted repository requires retyping the |
| 37 | encryption password. |
| 38 | To avoid excess password typing, consider using the "fossil shell" |
| 39 | command which prompts for the password just once, then reuses it for each |
| 40 | subsequent Fossil command entered at the prompt. |
| 41 | |
| 42 | On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not |
| 43 | (currently) work on an encrypted repository. |
| 44 | </blockquote> |
| 45 | <h2>Additional Security</h2><blockquote> |
| 46 | Use the FOSSIL_SECURITY_LEVEL environment for additional protection. |
| 47 |
+1
-1
| --- www/faq.tcl | ||
| +++ www/faq.tcl | ||
| @@ -157,11 +157,11 @@ | ||
| 157 | 157 | ############################################################################# |
| 158 | 158 | # Code to actually generate the FAQ |
| 159 | 159 | # |
| 160 | 160 | puts "<title>Fossil FAQ</title>" |
| 161 | 161 | puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n" |
| 162 | -puts "<p>Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n" | |
| 162 | +puts "Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n" | |
| 163 | 163 | |
| 164 | 164 | puts {<ol>} |
| 165 | 165 | for {set i 1} {$i<$cnt} {incr i} { |
| 166 | 166 | puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>" |
| 167 | 167 | } |
| 168 | 168 |
| --- www/faq.tcl | |
| +++ www/faq.tcl | |
| @@ -157,11 +157,11 @@ | |
| 157 | ############################################################################# |
| 158 | # Code to actually generate the FAQ |
| 159 | # |
| 160 | puts "<title>Fossil FAQ</title>" |
| 161 | puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n" |
| 162 | puts "<p>Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n" |
| 163 | |
| 164 | puts {<ol>} |
| 165 | for {set i 1} {$i<$cnt} {incr i} { |
| 166 | puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>" |
| 167 | } |
| 168 |
| --- www/faq.tcl | |
| +++ www/faq.tcl | |
| @@ -157,11 +157,11 @@ | |
| 157 | ############################################################################# |
| 158 | # Code to actually generate the FAQ |
| 159 | # |
| 160 | puts "<title>Fossil FAQ</title>" |
| 161 | puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n" |
| 162 | puts "Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n" |
| 163 | |
| 164 | puts {<ol>} |
| 165 | for {set i 1} {$i<$cnt} {incr i} { |
| 166 | puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>" |
| 167 | } |
| 168 |
+1
-1
| --- www/faq.wiki | ||
| +++ www/faq.wiki | ||
| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | <title>Fossil FAQ</title> |
| 2 | 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | 3 | |
| 4 | -<p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. | |
| 4 | +Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. | |
| 5 | 5 | |
| 6 | 6 | <ol> |
| 7 | 7 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 8 | 8 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 9 | 9 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 10 | 10 |
| --- www/faq.wiki | |
| +++ www/faq.wiki | |
| @@ -1,9 +1,9 @@ | |
| 1 | <title>Fossil FAQ</title> |
| 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | |
| 4 | <p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. |
| 5 | |
| 6 | <ol> |
| 7 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 8 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 9 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 10 |
| --- www/faq.wiki | |
| +++ www/faq.wiki | |
| @@ -1,9 +1,9 @@ | |
| 1 | <title>Fossil FAQ</title> |
| 2 | <h1 align="center">Frequently Asked Questions</h1> |
| 3 | |
| 4 | Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. |
| 5 | |
| 6 | <ol> |
| 7 | <li><a href="#q1">What GUIs are available for fossil?</a></li> |
| 8 | <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> |
| 9 | <li><a href="#q3">How do I create a new branch?</a></li> |
| 10 |
+60
-35
| --- www/fiveminutes.wiki | ||
| +++ www/fiveminutes.wiki | ||
| @@ -4,66 +4,91 @@ | ||
| 4 | 4 | The following document was contributed by Gilles Ganault on 2013-01-08. |
| 5 | 5 | </i></b> |
| 6 | 6 | </p><hr> |
| 7 | 7 | |
| 8 | 8 | <h1>Up and running in 5 minutes as a single user</h1> |
| 9 | -<p>This short document explains the main basic Fossil commands for a single | |
| 9 | + | |
| 10 | +This short document explains the main basic Fossil commands for a single | |
| 10 | 11 | user, i.e. with no additional users, with no need to synchronize with some remote |
| 11 | -repository, and no need for branching/forking.</p> | |
| 12 | +repository, and no need for branching/forking. | |
| 12 | 13 | |
| 13 | 14 | <h2>Create a new repository</h2> |
| 14 | -<p>fossil new c:\test.repo</p> | |
| 15 | -<p>This will create the new SQLite binary file that holds the repository, i.e. | |
| 15 | + | |
| 16 | +<tt>fossil new c:\test.repo</tt> | |
| 17 | + | |
| 18 | +This will create the new SQLite binary file that holds the repository, i.e. | |
| 16 | 19 | files, tickets, wiki, etc. It can be located anywhere, although it's considered |
| 17 | 20 | best practice to keep it outside the work directory where you will work on files |
| 18 | -after they've been checked out of the repository.</p> | |
| 21 | +after they've been checked out of the repository. | |
| 19 | 22 | |
| 20 | 23 | <h2>Open the repository</h2> |
| 21 | -<p>cd c:\temp\test.fossil</p> | |
| 22 | -<p>fossil open c:\test.repo</p> | |
| 23 | -<p>This will check out the last revision of all the files in the repository, | |
| 24 | + | |
| 25 | +<tt>cd c:\temp\test.fossil | |
| 26 | +<br> | |
| 27 | +fossil open c:\test.repo</tt> | |
| 28 | + | |
| 29 | +This will check out the last revision of all the files in the repository, | |
| 24 | 30 | if any, into the current work directory. In addition, it will create a binary |
| 25 | 31 | file _FOSSIL_ to keep track of changes (on non-Windows systems it is called |
| 26 | -<tt>.fslckout</tt>).</p> | |
| 32 | +<tt>.fslckout</tt>). | |
| 27 | 33 | |
| 28 | 34 | <h2>Add new files</h2> |
| 29 | -<p>fossil add .</p> | |
| 30 | -<p>To tell Fossil to add new files to the repository. The files aren't actually | |
| 31 | -added until you run "commit". When using ".", it tells Fossil | |
| 35 | + | |
| 36 | +<tt>fossil add .</tt> | |
| 37 | + | |
| 38 | +To tell Fossil to add new files to the repository. The files aren't actually | |
| 39 | +added until you run "<tt>fossil commit</tt>. When using ".", it tells Fossil | |
| 32 | 40 | to add all the files in the current directory recursively, i.e. including all |
| 33 | -the files in all the subdirectories.</p> | |
| 34 | -<p>Note: To tell Fossil to ignore some extensions:</p> | |
| 35 | -<p>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</p> | |
| 41 | +the files in all the subdirectories. | |
| 42 | + | |
| 43 | +Note: To tell Fossil to ignore some extensions: | |
| 44 | + | |
| 45 | +<tt>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</tt> | |
| 36 | 46 | |
| 37 | 47 | <h2>Remove files that haven't been committed yet</h2> |
| 38 | -<p>fossil delete myfile.c</p> | |
| 39 | -<p>This will simply remove the item from the list of files that were previously | |
| 40 | -added through "fossil add".</p> | |
| 48 | + | |
| 49 | +<tt>fossil delete myfile.c</tt> | |
| 50 | + | |
| 51 | +This will simply remove the item from the list of files that were previously | |
| 52 | +added through "<tt>fossil add</tt>". | |
| 41 | 53 | |
| 42 | 54 | <h2>Check current status</h2> |
| 43 | -<p>fossil changes</p> | |
| 44 | -<p>This shows the list of changes that have been done and will be committed the | |
| 45 | -next time you run "fossil commit". It's a useful command to run before | |
| 46 | -running "fossil commit" just to check that things are OK before proceeding.</p> | |
| 55 | + | |
| 56 | +<tt>fossil changes</tt> | |
| 57 | + | |
| 58 | +This shows the list of changes that have been done and will be committed the | |
| 59 | +next time you run "<tt>fossil commit</tt>". It's a useful command to run before | |
| 60 | +running "<tt>fossil commit</tt>" just to check that things are OK before proceeding. | |
| 47 | 61 | |
| 48 | 62 | <h2>Commit changes</h2> |
| 49 | -<p>To actually apply the pending changes to the repository, e.g. new files marked | |
| 63 | + | |
| 64 | +To actually apply the pending changes to the repository, e.g. new files marked | |
| 50 | 65 | for addition, checked-out files that have been edited and must be checked-in, |
| 51 | -etc.</p> | |
| 66 | +etc. | |
| 52 | 67 | |
| 53 | -<p>fossil commit -m "Added stuff"</p> | |
| 68 | +<tt>fossil commit -m "Added stuff"</tt> | |
| 54 | 69 | |
| 55 | 70 | If no file names are provided on the command-line then all changes will be checked in, |
| 56 | 71 | otherwise just the listed file(s) will be checked in. |
| 57 | 72 | |
| 58 | 73 | <h2>Compare two revisions of a file</h2> |
| 59 | -<p>If you wish to compare the last revision of a file and its checked out version | |
| 60 | -in your work directory:</p> | |
| 61 | -<p>fossil gdiff myfile.c</p> | |
| 62 | -<p>If you wish to compare two different revisions of a file in the repository:</p> | |
| 63 | -<p>fossil finfo myfile: Note the first hash, which is the hash of the commit | |
| 64 | -when the file was committed</p> | |
| 65 | -<p>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</p> | |
| 74 | + | |
| 75 | +If you wish to compare the last revision of a file and its checked out version | |
| 76 | +in your work directory: | |
| 77 | + | |
| 78 | +<tt>fossil gdiff myfile.c</tt> | |
| 79 | + | |
| 80 | +If you wish to compare two different revisions of a file in the repository: | |
| 81 | + | |
| 82 | +<tt>fossil finfo myfile</tt> | |
| 83 | + | |
| 84 | +Note the first hash, which is the hash of the commit | |
| 85 | +when the file was committed. | |
| 86 | + | |
| 87 | +<tt>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</tt> | |
| 88 | + | |
| 66 | 89 | <h2>Cancel changes and go back to previous revision</h2> |
| 67 | -<p>fossil revert myfile.c</p> | |
| 68 | -<p>Fossil does not prompt when reverting a file. It simply reminds the user about the | |
| 69 | -"undo" command, just in case the revert was a mistake.</p> | |
| 90 | + | |
| 91 | +<tt>fossil revert myfile.c</tt> | |
| 92 | + | |
| 93 | +Fossil does not prompt when reverting a file. It simply reminds the user about the | |
| 94 | +"undo" command, just in case the revert was a mistake. | |
| 70 | 95 |
| --- www/fiveminutes.wiki | |
| +++ www/fiveminutes.wiki | |
| @@ -4,66 +4,91 @@ | |
| 4 | The following document was contributed by Gilles Ganault on 2013-01-08. |
| 5 | </i></b> |
| 6 | </p><hr> |
| 7 | |
| 8 | <h1>Up and running in 5 minutes as a single user</h1> |
| 9 | <p>This short document explains the main basic Fossil commands for a single |
| 10 | user, i.e. with no additional users, with no need to synchronize with some remote |
| 11 | repository, and no need for branching/forking.</p> |
| 12 | |
| 13 | <h2>Create a new repository</h2> |
| 14 | <p>fossil new c:\test.repo</p> |
| 15 | <p>This will create the new SQLite binary file that holds the repository, i.e. |
| 16 | files, tickets, wiki, etc. It can be located anywhere, although it's considered |
| 17 | best practice to keep it outside the work directory where you will work on files |
| 18 | after they've been checked out of the repository.</p> |
| 19 | |
| 20 | <h2>Open the repository</h2> |
| 21 | <p>cd c:\temp\test.fossil</p> |
| 22 | <p>fossil open c:\test.repo</p> |
| 23 | <p>This will check out the last revision of all the files in the repository, |
| 24 | if any, into the current work directory. In addition, it will create a binary |
| 25 | file _FOSSIL_ to keep track of changes (on non-Windows systems it is called |
| 26 | <tt>.fslckout</tt>).</p> |
| 27 | |
| 28 | <h2>Add new files</h2> |
| 29 | <p>fossil add .</p> |
| 30 | <p>To tell Fossil to add new files to the repository. The files aren't actually |
| 31 | added until you run "commit". When using ".", it tells Fossil |
| 32 | to add all the files in the current directory recursively, i.e. including all |
| 33 | the files in all the subdirectories.</p> |
| 34 | <p>Note: To tell Fossil to ignore some extensions:</p> |
| 35 | <p>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</p> |
| 36 | |
| 37 | <h2>Remove files that haven't been committed yet</h2> |
| 38 | <p>fossil delete myfile.c</p> |
| 39 | <p>This will simply remove the item from the list of files that were previously |
| 40 | added through "fossil add".</p> |
| 41 | |
| 42 | <h2>Check current status</h2> |
| 43 | <p>fossil changes</p> |
| 44 | <p>This shows the list of changes that have been done and will be committed the |
| 45 | next time you run "fossil commit". It's a useful command to run before |
| 46 | running "fossil commit" just to check that things are OK before proceeding.</p> |
| 47 | |
| 48 | <h2>Commit changes</h2> |
| 49 | <p>To actually apply the pending changes to the repository, e.g. new files marked |
| 50 | for addition, checked-out files that have been edited and must be checked-in, |
| 51 | etc.</p> |
| 52 | |
| 53 | <p>fossil commit -m "Added stuff"</p> |
| 54 | |
| 55 | If no file names are provided on the command-line then all changes will be checked in, |
| 56 | otherwise just the listed file(s) will be checked in. |
| 57 | |
| 58 | <h2>Compare two revisions of a file</h2> |
| 59 | <p>If you wish to compare the last revision of a file and its checked out version |
| 60 | in your work directory:</p> |
| 61 | <p>fossil gdiff myfile.c</p> |
| 62 | <p>If you wish to compare two different revisions of a file in the repository:</p> |
| 63 | <p>fossil finfo myfile: Note the first hash, which is the hash of the commit |
| 64 | when the file was committed</p> |
| 65 | <p>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</p> |
| 66 | <h2>Cancel changes and go back to previous revision</h2> |
| 67 | <p>fossil revert myfile.c</p> |
| 68 | <p>Fossil does not prompt when reverting a file. It simply reminds the user about the |
| 69 | "undo" command, just in case the revert was a mistake.</p> |
| 70 |
| --- www/fiveminutes.wiki | |
| +++ www/fiveminutes.wiki | |
| @@ -4,66 +4,91 @@ | |
| 4 | The following document was contributed by Gilles Ganault on 2013-01-08. |
| 5 | </i></b> |
| 6 | </p><hr> |
| 7 | |
| 8 | <h1>Up and running in 5 minutes as a single user</h1> |
| 9 | |
| 10 | This short document explains the main basic Fossil commands for a single |
| 11 | user, i.e. with no additional users, with no need to synchronize with some remote |
| 12 | repository, and no need for branching/forking. |
| 13 | |
| 14 | <h2>Create a new repository</h2> |
| 15 | |
| 16 | <tt>fossil new c:\test.repo</tt> |
| 17 | |
| 18 | This will create the new SQLite binary file that holds the repository, i.e. |
| 19 | files, tickets, wiki, etc. It can be located anywhere, although it's considered |
| 20 | best practice to keep it outside the work directory where you will work on files |
| 21 | after they've been checked out of the repository. |
| 22 | |
| 23 | <h2>Open the repository</h2> |
| 24 | |
| 25 | <tt>cd c:\temp\test.fossil |
| 26 | <br> |
| 27 | fossil open c:\test.repo</tt> |
| 28 | |
| 29 | This will check out the last revision of all the files in the repository, |
| 30 | if any, into the current work directory. In addition, it will create a binary |
| 31 | file _FOSSIL_ to keep track of changes (on non-Windows systems it is called |
| 32 | <tt>.fslckout</tt>). |
| 33 | |
| 34 | <h2>Add new files</h2> |
| 35 | |
| 36 | <tt>fossil add .</tt> |
| 37 | |
| 38 | To tell Fossil to add new files to the repository. The files aren't actually |
| 39 | added until you run "<tt>fossil commit</tt>. When using ".", it tells Fossil |
| 40 | to add all the files in the current directory recursively, i.e. including all |
| 41 | the files in all the subdirectories. |
| 42 | |
| 43 | Note: To tell Fossil to ignore some extensions: |
| 44 | |
| 45 | <tt>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</tt> |
| 46 | |
| 47 | <h2>Remove files that haven't been committed yet</h2> |
| 48 | |
| 49 | <tt>fossil delete myfile.c</tt> |
| 50 | |
| 51 | This will simply remove the item from the list of files that were previously |
| 52 | added through "<tt>fossil add</tt>". |
| 53 | |
| 54 | <h2>Check current status</h2> |
| 55 | |
| 56 | <tt>fossil changes</tt> |
| 57 | |
| 58 | This shows the list of changes that have been done and will be committed the |
| 59 | next time you run "<tt>fossil commit</tt>". It's a useful command to run before |
| 60 | running "<tt>fossil commit</tt>" just to check that things are OK before proceeding. |
| 61 | |
| 62 | <h2>Commit changes</h2> |
| 63 | |
| 64 | To actually apply the pending changes to the repository, e.g. new files marked |
| 65 | for addition, checked-out files that have been edited and must be checked-in, |
| 66 | etc. |
| 67 | |
| 68 | <tt>fossil commit -m "Added stuff"</tt> |
| 69 | |
| 70 | If no file names are provided on the command-line then all changes will be checked in, |
| 71 | otherwise just the listed file(s) will be checked in. |
| 72 | |
| 73 | <h2>Compare two revisions of a file</h2> |
| 74 | |
| 75 | If you wish to compare the last revision of a file and its checked out version |
| 76 | in your work directory: |
| 77 | |
| 78 | <tt>fossil gdiff myfile.c</tt> |
| 79 | |
| 80 | If you wish to compare two different revisions of a file in the repository: |
| 81 | |
| 82 | <tt>fossil finfo myfile</tt> |
| 83 | |
| 84 | Note the first hash, which is the hash of the commit |
| 85 | when the file was committed. |
| 86 | |
| 87 | <tt>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</tt> |
| 88 | |
| 89 | <h2>Cancel changes and go back to previous revision</h2> |
| 90 | |
| 91 | <tt>fossil revert myfile.c</tt> |
| 92 | |
| 93 | Fossil does not prompt when reverting a file. It simply reminds the user about the |
| 94 | "undo" command, just in case the revert was a mistake. |
| 95 |
+27
-27
| --- www/foss-cklist.wiki | ||
| +++ www/foss-cklist.wiki | ||
| @@ -1,25 +1,25 @@ | ||
| 1 | 1 | <title>Checklist For Successful Open-Source Projects</title> |
| 2 | 2 | <nowiki> |
| 3 | 3 | |
| 4 | -<p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score | |
| 4 | +This checklist is loosely derived from Tom "Spot" Callaway's Fail Score | |
| 5 | 5 | blog post <a href="http://spot.livejournal.com/308370.html"> |
| 6 | 6 | http://spot.livejournal.com/308370.html</a> (see also |
| 7 | 7 | <a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a>). |
| 8 | 8 | Tom's original post assigned point scores to the various elements and |
| 9 | 9 | by adding together the individual points, the reader is supposed to be able |
| 10 | 10 | to judge the likelihood that the project will fail. |
| 11 | 11 | The point scores, and the items on the list, clearly reflect Tom's |
| 12 | 12 | biases and are not necessarily those of the larger open-source community. |
| 13 | 13 | Nevertheless, the policy of the Fossil shall be to strive for a perfect |
| 14 | -score.</p> | |
| 14 | +score. | |
| 15 | 15 | |
| 16 | -<p>This checklist is an inversion of Tom's original post in that it strives to | |
| 16 | +This checklist is an inversion of Tom's original post in that it strives to | |
| 17 | 17 | say what the project should do in order to succeed rather than what it |
| 18 | -should not do to avoid failure. The point values are omitted.</p> | |
| 18 | +should not do to avoid failure. The point values are omitted. | |
| 19 | 19 | |
| 20 | -<p>See also: | |
| 20 | +See also: | |
| 21 | 21 | <ul> |
| 22 | 22 | <li><a href="http://offog.org/articles/packaging/"> |
| 23 | 23 | http://offog.org/articles/packaging/</a> |
| 24 | 24 | <li> |
| 25 | 25 | <a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases"> |
| @@ -27,36 +27,36 @@ | ||
| 27 | 27 | </ul> |
| 28 | 28 | |
| 29 | 29 | <hr> |
| 30 | 30 | |
| 31 | 31 | <ol> |
| 32 | -<li><p>The source code size is less than 100 MB, uncompressed. | |
| 32 | +<li>The source code size is less than 100 MB, uncompressed. | |
| 33 | 33 | |
| 34 | -<li><p>The project uses a Version Control System (VCS). | |
| 34 | +<li>The project uses a Version Control System (VCS). | |
| 35 | 35 | <ol type="a"> |
| 36 | 36 | <li>The VCS has a working web interface. |
| 37 | 37 | <li>There is documentation on how to use the VCS. |
| 38 | 38 | <li>The VCS is general-purpose, not something hacked together for this |
| 39 | 39 | one project. |
| 40 | 40 | </ol> |
| 41 | 41 | |
| 42 | -<li><p>The project comes with documentation on how to build from source | |
| 42 | +<li>The project comes with documentation on how to build from source | |
| 43 | 43 | and that documentation is lucid, correct, and up-to-date. |
| 44 | 44 | |
| 45 | -<li><p>The project is configurable using an autoconf-generated configure | |
| 45 | +<li>The project is configurable using an autoconf-generated configure | |
| 46 | 46 | script, or the equivalent, and does not require: |
| 47 | 47 | <ol type="a"> |
| 48 | 48 | <li>Manually editing flat files |
| 49 | 49 | <li>Editing code header files |
| 50 | 50 | </ol> |
| 51 | 51 | |
| 52 | -<li><p>The project should be buildable using commonly available and | |
| 52 | +<li>The project should be buildable using commonly available and | |
| 53 | 53 | standard tools like "make". |
| 54 | 54 | |
| 55 | -<li><p>The project does not depend on 3rd-party proprietary build tools. | |
| 55 | +<li>The project does not depend on 3rd-party proprietary build tools. | |
| 56 | 56 | |
| 57 | -<li><p>The project is able to dynamically link against standard libraries | |
| 57 | +<li>The project is able to dynamically link against standard libraries | |
| 58 | 58 | such as zlib and libjpeg. |
| 59 | 59 | <ol type="a"> |
| 60 | 60 | <li>The project does not ship with the sources to standard libraries. |
| 61 | 61 | <i>(On the Fossil project, we will allow the SQLite library sources |
| 62 | 62 | to be included in the source tree as long as a system-installed |
| @@ -65,48 +65,48 @@ | ||
| 65 | 65 | libraries. Any required bug fixes in standard libraries are pushed |
| 66 | 66 | back upstream. |
| 67 | 67 | </ol> |
| 68 | 68 | |
| 69 | 69 | |
| 70 | -<li><p>"make install" works and can be configured to target any | |
| 70 | +<li>"make install" works and can be configured to target any | |
| 71 | 71 | directory of the installer's choosing. |
| 72 | 72 | <ol type="a"> |
| 73 | 73 | <li>The project contains no unconfigurable hard-coded pathnames like |
| 74 | 74 | "/opt" or "/usr/local". |
| 75 | 75 | <li>After installation, the source tree can be moved or deleted and |
| 76 | 76 | the application will continue working. |
| 77 | 77 | </ol> |
| 78 | 78 | |
| 79 | 79 | |
| 80 | -<li><p>The source code uses only \n for line endings, not \r\n. | |
| 80 | +<li>The source code uses only \n for line endings, not \r\n. | |
| 81 | 81 | |
| 82 | -<li><p>The code does not depend on any special compiler features or bugs. | |
| 82 | +<li>The code does not depend on any special compiler features or bugs. | |
| 83 | 83 | |
| 84 | -<li><p>The project has a mailing list and announces releases on | |
| 84 | +<li>The project has a mailing list and announces releases on | |
| 85 | 85 | the mailing list. |
| 86 | 86 | |
| 87 | -<li><p>The project has a bug tracker. | |
| 87 | +<li>The project has a bug tracker. | |
| 88 | + | |
| 89 | +<li>The project has a website. | |
| 88 | 90 | |
| 89 | -<li><p>The project has a website. | |
| 91 | +<li>Release version numbers are in the traditional X.Y or X.Y.Z format. | |
| 90 | 92 | |
| 91 | -<li><p>Release version numbers are in the traditional X.Y or X.Y.Z format. | |
| 92 | - | |
| 93 | -<li><p>Releases can be downloaded as tarball using | |
| 93 | +<li>Releases can be downloaded as tarball using | |
| 94 | 94 | gzip or bzip2 compression. |
| 95 | 95 | |
| 96 | -<li><p>Releases unpack into a versioned top-level directory. | |
| 96 | +<li>Releases unpack into a versioned top-level directory. | |
| 97 | 97 | (ex: "projectname-1.2.3/"). |
| 98 | 98 | |
| 99 | -<li><p>A statement of license appears at the top of every source code file | |
| 99 | +<li>A statement of license appears at the top of every source code file | |
| 100 | 100 | and the complete text of the license is included in the source code |
| 101 | 101 | tarball. |
| 102 | 102 | |
| 103 | -<li><p>There are no incompatible licenses in the code. | |
| 103 | +<li>There are no incompatible licenses in the code. | |
| 104 | 104 | |
| 105 | -<li><p>The project has not been blithely proclaimed "public domain" without | |
| 105 | +<li>The project has not been blithely proclaimed "public domain" without | |
| 106 | 106 | having gone through the tedious and exacting legal steps to actually put it |
| 107 | 107 | in the public domain. |
| 108 | 108 | |
| 109 | -<li><p>There is an accurate change log in the code and on the website. | |
| 109 | +<li>There is an accurate change log in the code and on the website. | |
| 110 | 110 | |
| 111 | -<li><p>There is documentation in the code and on the website. | |
| 111 | +<li>There is documentation in the code and on the website. | |
| 112 | 112 | </ol> |
| 113 | 113 |
| --- www/foss-cklist.wiki | |
| +++ www/foss-cklist.wiki | |
| @@ -1,25 +1,25 @@ | |
| 1 | <title>Checklist For Successful Open-Source Projects</title> |
| 2 | <nowiki> |
| 3 | |
| 4 | <p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score |
| 5 | blog post <a href="http://spot.livejournal.com/308370.html"> |
| 6 | http://spot.livejournal.com/308370.html</a> (see also |
| 7 | <a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a>). |
| 8 | Tom's original post assigned point scores to the various elements and |
| 9 | by adding together the individual points, the reader is supposed to be able |
| 10 | to judge the likelihood that the project will fail. |
| 11 | The point scores, and the items on the list, clearly reflect Tom's |
| 12 | biases and are not necessarily those of the larger open-source community. |
| 13 | Nevertheless, the policy of the Fossil shall be to strive for a perfect |
| 14 | score.</p> |
| 15 | |
| 16 | <p>This checklist is an inversion of Tom's original post in that it strives to |
| 17 | say what the project should do in order to succeed rather than what it |
| 18 | should not do to avoid failure. The point values are omitted.</p> |
| 19 | |
| 20 | <p>See also: |
| 21 | <ul> |
| 22 | <li><a href="http://offog.org/articles/packaging/"> |
| 23 | http://offog.org/articles/packaging/</a> |
| 24 | <li> |
| 25 | <a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases"> |
| @@ -27,36 +27,36 @@ | |
| 27 | </ul> |
| 28 | |
| 29 | <hr> |
| 30 | |
| 31 | <ol> |
| 32 | <li><p>The source code size is less than 100 MB, uncompressed. |
| 33 | |
| 34 | <li><p>The project uses a Version Control System (VCS). |
| 35 | <ol type="a"> |
| 36 | <li>The VCS has a working web interface. |
| 37 | <li>There is documentation on how to use the VCS. |
| 38 | <li>The VCS is general-purpose, not something hacked together for this |
| 39 | one project. |
| 40 | </ol> |
| 41 | |
| 42 | <li><p>The project comes with documentation on how to build from source |
| 43 | and that documentation is lucid, correct, and up-to-date. |
| 44 | |
| 45 | <li><p>The project is configurable using an autoconf-generated configure |
| 46 | script, or the equivalent, and does not require: |
| 47 | <ol type="a"> |
| 48 | <li>Manually editing flat files |
| 49 | <li>Editing code header files |
| 50 | </ol> |
| 51 | |
| 52 | <li><p>The project should be buildable using commonly available and |
| 53 | standard tools like "make". |
| 54 | |
| 55 | <li><p>The project does not depend on 3rd-party proprietary build tools. |
| 56 | |
| 57 | <li><p>The project is able to dynamically link against standard libraries |
| 58 | such as zlib and libjpeg. |
| 59 | <ol type="a"> |
| 60 | <li>The project does not ship with the sources to standard libraries. |
| 61 | <i>(On the Fossil project, we will allow the SQLite library sources |
| 62 | to be included in the source tree as long as a system-installed |
| @@ -65,48 +65,48 @@ | |
| 65 | libraries. Any required bug fixes in standard libraries are pushed |
| 66 | back upstream. |
| 67 | </ol> |
| 68 | |
| 69 | |
| 70 | <li><p>"make install" works and can be configured to target any |
| 71 | directory of the installer's choosing. |
| 72 | <ol type="a"> |
| 73 | <li>The project contains no unconfigurable hard-coded pathnames like |
| 74 | "/opt" or "/usr/local". |
| 75 | <li>After installation, the source tree can be moved or deleted and |
| 76 | the application will continue working. |
| 77 | </ol> |
| 78 | |
| 79 | |
| 80 | <li><p>The source code uses only \n for line endings, not \r\n. |
| 81 | |
| 82 | <li><p>The code does not depend on any special compiler features or bugs. |
| 83 | |
| 84 | <li><p>The project has a mailing list and announces releases on |
| 85 | the mailing list. |
| 86 | |
| 87 | <li><p>The project has a bug tracker. |
| 88 | |
| 89 | <li><p>The project has a website. |
| 90 | |
| 91 | <li><p>Release version numbers are in the traditional X.Y or X.Y.Z format. |
| 92 | |
| 93 | <li><p>Releases can be downloaded as tarball using |
| 94 | gzip or bzip2 compression. |
| 95 | |
| 96 | <li><p>Releases unpack into a versioned top-level directory. |
| 97 | (ex: "projectname-1.2.3/"). |
| 98 | |
| 99 | <li><p>A statement of license appears at the top of every source code file |
| 100 | and the complete text of the license is included in the source code |
| 101 | tarball. |
| 102 | |
| 103 | <li><p>There are no incompatible licenses in the code. |
| 104 | |
| 105 | <li><p>The project has not been blithely proclaimed "public domain" without |
| 106 | having gone through the tedious and exacting legal steps to actually put it |
| 107 | in the public domain. |
| 108 | |
| 109 | <li><p>There is an accurate change log in the code and on the website. |
| 110 | |
| 111 | <li><p>There is documentation in the code and on the website. |
| 112 | </ol> |
| 113 |
| --- www/foss-cklist.wiki | |
| +++ www/foss-cklist.wiki | |
| @@ -1,25 +1,25 @@ | |
| 1 | <title>Checklist For Successful Open-Source Projects</title> |
| 2 | <nowiki> |
| 3 | |
| 4 | This checklist is loosely derived from Tom "Spot" Callaway's Fail Score |
| 5 | blog post <a href="http://spot.livejournal.com/308370.html"> |
| 6 | http://spot.livejournal.com/308370.html</a> (see also |
| 7 | <a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a>). |
| 8 | Tom's original post assigned point scores to the various elements and |
| 9 | by adding together the individual points, the reader is supposed to be able |
| 10 | to judge the likelihood that the project will fail. |
| 11 | The point scores, and the items on the list, clearly reflect Tom's |
| 12 | biases and are not necessarily those of the larger open-source community. |
| 13 | Nevertheless, the policy of the Fossil shall be to strive for a perfect |
| 14 | score. |
| 15 | |
| 16 | This checklist is an inversion of Tom's original post in that it strives to |
| 17 | say what the project should do in order to succeed rather than what it |
| 18 | should not do to avoid failure. The point values are omitted. |
| 19 | |
| 20 | See also: |
| 21 | <ul> |
| 22 | <li><a href="http://offog.org/articles/packaging/"> |
| 23 | http://offog.org/articles/packaging/</a> |
| 24 | <li> |
| 25 | <a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases"> |
| @@ -27,36 +27,36 @@ | |
| 27 | </ul> |
| 28 | |
| 29 | <hr> |
| 30 | |
| 31 | <ol> |
| 32 | <li>The source code size is less than 100 MB, uncompressed. |
| 33 | |
| 34 | <li>The project uses a Version Control System (VCS). |
| 35 | <ol type="a"> |
| 36 | <li>The VCS has a working web interface. |
| 37 | <li>There is documentation on how to use the VCS. |
| 38 | <li>The VCS is general-purpose, not something hacked together for this |
| 39 | one project. |
| 40 | </ol> |
| 41 | |
| 42 | <li>The project comes with documentation on how to build from source |
| 43 | and that documentation is lucid, correct, and up-to-date. |
| 44 | |
| 45 | <li>The project is configurable using an autoconf-generated configure |
| 46 | script, or the equivalent, and does not require: |
| 47 | <ol type="a"> |
| 48 | <li>Manually editing flat files |
| 49 | <li>Editing code header files |
| 50 | </ol> |
| 51 | |
| 52 | <li>The project should be buildable using commonly available and |
| 53 | standard tools like "make". |
| 54 | |
| 55 | <li>The project does not depend on 3rd-party proprietary build tools. |
| 56 | |
| 57 | <li>The project is able to dynamically link against standard libraries |
| 58 | such as zlib and libjpeg. |
| 59 | <ol type="a"> |
| 60 | <li>The project does not ship with the sources to standard libraries. |
| 61 | <i>(On the Fossil project, we will allow the SQLite library sources |
| 62 | to be included in the source tree as long as a system-installed |
| @@ -65,48 +65,48 @@ | |
| 65 | libraries. Any required bug fixes in standard libraries are pushed |
| 66 | back upstream. |
| 67 | </ol> |
| 68 | |
| 69 | |
| 70 | <li>"make install" works and can be configured to target any |
| 71 | directory of the installer's choosing. |
| 72 | <ol type="a"> |
| 73 | <li>The project contains no unconfigurable hard-coded pathnames like |
| 74 | "/opt" or "/usr/local". |
| 75 | <li>After installation, the source tree can be moved or deleted and |
| 76 | the application will continue working. |
| 77 | </ol> |
| 78 | |
| 79 | |
| 80 | <li>The source code uses only \n for line endings, not \r\n. |
| 81 | |
| 82 | <li>The code does not depend on any special compiler features or bugs. |
| 83 | |
| 84 | <li>The project has a mailing list and announces releases on |
| 85 | the mailing list. |
| 86 | |
| 87 | <li>The project has a bug tracker. |
| 88 | |
| 89 | <li>The project has a website. |
| 90 | |
| 91 | <li>Release version numbers are in the traditional X.Y or X.Y.Z format. |
| 92 | |
| 93 | <li>Releases can be downloaded as tarball using |
| 94 | gzip or bzip2 compression. |
| 95 | |
| 96 | <li>Releases unpack into a versioned top-level directory. |
| 97 | (ex: "projectname-1.2.3/"). |
| 98 | |
| 99 | <li>A statement of license appears at the top of every source code file |
| 100 | and the complete text of the license is included in the source code |
| 101 | tarball. |
| 102 | |
| 103 | <li>There are no incompatible licenses in the code. |
| 104 | |
| 105 | <li>The project has not been blithely proclaimed "public domain" without |
| 106 | having gone through the tedious and exacting legal steps to actually put it |
| 107 | in the public domain. |
| 108 | |
| 109 | <li>There is an accurate change log in the code and on the website. |
| 110 | |
| 111 | <li>There is documentation in the code and on the website. |
| 112 | </ol> |
| 113 |
+70
-72
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -424,82 +424,80 @@ | ||
| 424 | 424 | |
| 425 | 425 | Fossil's normal mode of operation differs on every one of these points, |
| 426 | 426 | with the specific designed-in goal of promoting SQLite's cathedral |
| 427 | 427 | development model: |
| 428 | 428 | |
| 429 | -<ul> | |
| 430 | - <li><p><b>Personal engagement:</b> SQLite's developers know each | |
| 431 | - other by name and work together daily on the project.</p></li> | |
| 432 | - | |
| 433 | - <li><p><b>Trust over hierarchy:</b> SQLite's developers check | |
| 434 | - changes into their local repository, and these are immediately and | |
| 435 | - automatically synchronized up to the central repository; there is no | |
| 436 | - "[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator | |
| 437 | - and lieutenants]" hierarchy as with Linux kernel contributions. D. | |
| 438 | - Richard Hipp rarely overrides decisions made by those he has trusted | |
| 439 | - with commit access on his repositories. Fossil allows you to give | |
| 440 | - [./caps/admin-v-setup.md|some users] more power over what | |
| 441 | - they can do with the repository, but Fossil [./caps/index.md#ucap | | |
| 442 | - only loosely supports] the enforcement of a development organization's | |
| 443 | - social and power hierarchies. Fossil is a great fit for | |
| 444 | - [https://en.wikipedia.org/wiki/Flat_organization|flat | |
| 445 | - organizations].</p></li> | |
| 446 | - | |
| 447 | - <li><p><b>No easy drive-by contributions:</b> Git | |
| 448 | - [https://www.git-scm.com/docs/git-request-pull|pull requests] offer | |
| 449 | - a low-friction path to accepting | |
| 450 | - [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by | |
| 451 | - contributions]. Fossil's closest equivalents are its unique | |
| 452 | - [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement | |
| 453 | - than firing off a PR.⁷ This difference comes directly from the | |
| 454 | - initial designed purpose for each tool: the SQLite project doesn't | |
| 455 | - accept outside contributions from previously-unknown developers, but | |
| 456 | - the Linux kernel does.</p></li> | |
| 457 | - | |
| 458 | - <li><p><b>No rebasing:</b> When your local repo clone syncs changes | |
| 459 | - up to its parent, those changes are sent exactly as they were | |
| 460 | - committed locally. [#history|There is no rebasing mechanism in | |
| 461 | - Fossil, on purpose.]</p></li> | |
| 462 | - | |
| 463 | - <li><p><b>Sync over push:</b> Explicit pushes are uncommon in | |
| 464 | - Fossil-based projects: the default is to rely on | |
| 465 | - [/help?cmd=autosync|autosync mode] instead, in which each commit | |
| 466 | - syncs immediately to its parent repository. This is a mode so you | |
| 467 | - can turn it off temporarily when needed, such as when working | |
| 468 | - offline. Fossil is still a truly distributed version control system; | |
| 469 | - it's just that its starting default is to assume you're rarely out | |
| 470 | - of communication with the parent repo. | |
| 471 | - <br><br> | |
| 472 | - This is not merely a reflection of modern always-connected computing | |
| 473 | - environments. It is a conscious decision in direct support of | |
| 474 | - SQLite's cathedral development model: we don't want developers going | |
| 475 | - dark, then showing up weeks later with a massive bolus of changes | |
| 476 | - for us to integrate all at once. | |
| 477 | - [https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy] | |
| 478 | - put it well in his book on software project management, | |
| 479 | - <i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software | |
| 480 | - Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware | |
| 481 | - of a guy in a room]."</p></li> | |
| 482 | - | |
| 483 | - <li><p><b>Branch names sync:</b> Unlike in Git, branch names in | |
| 484 | - Fossil are not purely local labels. They sync along with everything | |
| 485 | - else, so everyone sees the same set of branch names. Fossil's design | |
| 486 | - choice here is a direct reflection of the Linux vs. SQLite project | |
| 487 | - outlook: SQLite's developers collaborate closely on a single | |
| 488 | - coherent project, whereas Linux's developers go off on tangents and | |
| 489 | - occasionally send selected change sets to each other.</p></li> | |
| 490 | - | |
| 491 | - <li><p><b>Private branches are rare:</b> | |
| 492 | - [/doc/trunk/www/private.wiki|Private branches exist in Fossil], but | |
| 493 | - they're normally used to handle rare exception cases, whereas in | |
| 494 | - many Git projects, they're part of the straight-line development | |
| 495 | - process.</p></li> | |
| 496 | - | |
| 497 | - <li><p><b>Identical clones:</b> Fossil's autosync system tries to | |
| 498 | - keep each local clone identical to the repository it cloned | |
| 499 | - from.</p></li> | |
| 500 | -</ul> | |
| 429 | + * <b>Personal engagement:</b> SQLite's developers know each | |
| 430 | + other by name and work together daily on the project. | |
| 431 | + | |
| 432 | + * <b>Trust over hierarchy:</b> SQLite's developers check | |
| 433 | + changes into their local repository, and these are immediately and | |
| 434 | + automatically synchronized up to the central repository; there is no | |
| 435 | + "[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator | |
| 436 | + and lieutenants]" hierarchy as with Linux kernel contributions. D. | |
| 437 | + Richard Hipp rarely overrides decisions made by those he has trusted | |
| 438 | + with commit access on his repositories. Fossil allows you to give | |
| 439 | + [./caps/admin-v-setup.md|some users] more power over what | |
| 440 | + they can do with the repository, but Fossil [./caps/index.md#ucap | | |
| 441 | + only loosely supports] the enforcement of a development organization's | |
| 442 | + social and power hierarchies. Fossil is a great fit for | |
| 443 | + [https://en.wikipedia.org/wiki/Flat_organization|flat | |
| 444 | + organizations]. | |
| 445 | + | |
| 446 | + * <b>No easy drive-by contributions:</b> Git | |
| 447 | + [https://www.git-scm.com/docs/git-request-pull|pull requests] offer | |
| 448 | + a low-friction path to accepting | |
| 449 | + [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by | |
| 450 | + contributions]. Fossil's closest equivalents are its unique | |
| 451 | + [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement | |
| 452 | + than firing off a PR.⁷ This difference comes directly from the | |
| 453 | + initial designed purpose for each tool: the SQLite project doesn't | |
| 454 | + accept outside contributions from previously-unknown developers, but | |
| 455 | + the Linux kernel does. | |
| 456 | + | |
| 457 | + * <b>No rebasing:</b> When your local repo clone syncs changes | |
| 458 | + up to its parent, those changes are sent exactly as they were | |
| 459 | + committed locally. [#history|There is no rebasing mechanism in | |
| 460 | + Fossil, on purpose.] | |
| 461 | + | |
| 462 | + * <b>Sync over push:</b> Explicit pushes are uncommon in | |
| 463 | + Fossil-based projects: the default is to rely on | |
| 464 | + [/help?cmd=autosync|autosync mode] instead, in which each commit | |
| 465 | + syncs immediately to its parent repository. This is a mode so you | |
| 466 | + can turn it off temporarily when needed, such as when working | |
| 467 | + offline. Fossil is still a truly distributed version control system; | |
| 468 | + it's just that its starting default is to assume you're rarely out | |
| 469 | + of communication with the parent repo. | |
| 470 | + <br><br> | |
| 471 | + This is not merely a reflection of modern always-connected computing | |
| 472 | + environments. It is a conscious decision in direct support of | |
| 473 | + SQLite's cathedral development model: we don't want developers going | |
| 474 | + dark, then showing up weeks later with a massive bolus of changes | |
| 475 | + for us to integrate all at once. | |
| 476 | + [https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy] | |
| 477 | + put it well in his book on software project management, | |
| 478 | + <i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software | |
| 479 | + Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware | |
| 480 | + of a guy in a room]." | |
| 481 | + | |
| 482 | + * <b>Branch names sync:</b> Unlike in Git, branch names in | |
| 483 | + Fossil are not purely local labels. They sync along with everything | |
| 484 | + else, so everyone sees the same set of branch names. Fossil's design | |
| 485 | + choice here is a direct reflection of the Linux vs. SQLite project | |
| 486 | + outlook: SQLite's developers collaborate closely on a single | |
| 487 | + coherent project, whereas Linux's developers go off on tangents and | |
| 488 | + occasionally send selected change sets to each other. | |
| 489 | + | |
| 490 | + * <b>Private branches are rare:</b> | |
| 491 | + [/doc/trunk/www/private.wiki|Private branches exist in Fossil], but | |
| 492 | + they're normally used to handle rare exception cases, whereas in | |
| 493 | + many Git projects, they're part of the straight-line development | |
| 494 | + process. | |
| 495 | + | |
| 496 | + * <b>Identical clones:</b> Fossil's autosync system tries to | |
| 497 | + keep each local clone identical to the repository it cloned | |
| 498 | + from. | |
| 501 | 499 | |
| 502 | 500 | Where Git encourages siloed development, Fossil fights against it. |
| 503 | 501 | Fossil places a lot of emphasis on synchronizing everyone's work and on |
| 504 | 502 | reporting on the state of the project and the work of its developers, so |
| 505 | 503 | that everyone — especially the project leader — can maintain a better |
| 506 | 504 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -424,82 +424,80 @@ | |
| 424 | |
| 425 | Fossil's normal mode of operation differs on every one of these points, |
| 426 | with the specific designed-in goal of promoting SQLite's cathedral |
| 427 | development model: |
| 428 | |
| 429 | <ul> |
| 430 | <li><p><b>Personal engagement:</b> SQLite's developers know each |
| 431 | other by name and work together daily on the project.</p></li> |
| 432 | |
| 433 | <li><p><b>Trust over hierarchy:</b> SQLite's developers check |
| 434 | changes into their local repository, and these are immediately and |
| 435 | automatically synchronized up to the central repository; there is no |
| 436 | "[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator |
| 437 | and lieutenants]" hierarchy as with Linux kernel contributions. D. |
| 438 | Richard Hipp rarely overrides decisions made by those he has trusted |
| 439 | with commit access on his repositories. Fossil allows you to give |
| 440 | [./caps/admin-v-setup.md|some users] more power over what |
| 441 | they can do with the repository, but Fossil [./caps/index.md#ucap | |
| 442 | only loosely supports] the enforcement of a development organization's |
| 443 | social and power hierarchies. Fossil is a great fit for |
| 444 | [https://en.wikipedia.org/wiki/Flat_organization|flat |
| 445 | organizations].</p></li> |
| 446 | |
| 447 | <li><p><b>No easy drive-by contributions:</b> Git |
| 448 | [https://www.git-scm.com/docs/git-request-pull|pull requests] offer |
| 449 | a low-friction path to accepting |
| 450 | [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by |
| 451 | contributions]. Fossil's closest equivalents are its unique |
| 452 | [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement |
| 453 | than firing off a PR.⁷ This difference comes directly from the |
| 454 | initial designed purpose for each tool: the SQLite project doesn't |
| 455 | accept outside contributions from previously-unknown developers, but |
| 456 | the Linux kernel does.</p></li> |
| 457 | |
| 458 | <li><p><b>No rebasing:</b> When your local repo clone syncs changes |
| 459 | up to its parent, those changes are sent exactly as they were |
| 460 | committed locally. [#history|There is no rebasing mechanism in |
| 461 | Fossil, on purpose.]</p></li> |
| 462 | |
| 463 | <li><p><b>Sync over push:</b> Explicit pushes are uncommon in |
| 464 | Fossil-based projects: the default is to rely on |
| 465 | [/help?cmd=autosync|autosync mode] instead, in which each commit |
| 466 | syncs immediately to its parent repository. This is a mode so you |
| 467 | can turn it off temporarily when needed, such as when working |
| 468 | offline. Fossil is still a truly distributed version control system; |
| 469 | it's just that its starting default is to assume you're rarely out |
| 470 | of communication with the parent repo. |
| 471 | <br><br> |
| 472 | This is not merely a reflection of modern always-connected computing |
| 473 | environments. It is a conscious decision in direct support of |
| 474 | SQLite's cathedral development model: we don't want developers going |
| 475 | dark, then showing up weeks later with a massive bolus of changes |
| 476 | for us to integrate all at once. |
| 477 | [https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy] |
| 478 | put it well in his book on software project management, |
| 479 | <i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software |
| 480 | Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware |
| 481 | of a guy in a room]."</p></li> |
| 482 | |
| 483 | <li><p><b>Branch names sync:</b> Unlike in Git, branch names in |
| 484 | Fossil are not purely local labels. They sync along with everything |
| 485 | else, so everyone sees the same set of branch names. Fossil's design |
| 486 | choice here is a direct reflection of the Linux vs. SQLite project |
| 487 | outlook: SQLite's developers collaborate closely on a single |
| 488 | coherent project, whereas Linux's developers go off on tangents and |
| 489 | occasionally send selected change sets to each other.</p></li> |
| 490 | |
| 491 | <li><p><b>Private branches are rare:</b> |
| 492 | [/doc/trunk/www/private.wiki|Private branches exist in Fossil], but |
| 493 | they're normally used to handle rare exception cases, whereas in |
| 494 | many Git projects, they're part of the straight-line development |
| 495 | process.</p></li> |
| 496 | |
| 497 | <li><p><b>Identical clones:</b> Fossil's autosync system tries to |
| 498 | keep each local clone identical to the repository it cloned |
| 499 | from.</p></li> |
| 500 | </ul> |
| 501 | |
| 502 | Where Git encourages siloed development, Fossil fights against it. |
| 503 | Fossil places a lot of emphasis on synchronizing everyone's work and on |
| 504 | reporting on the state of the project and the work of its developers, so |
| 505 | that everyone — especially the project leader — can maintain a better |
| 506 |
| --- www/fossil-v-git.wiki | |
| +++ www/fossil-v-git.wiki | |
| @@ -424,82 +424,80 @@ | |
| 424 | |
| 425 | Fossil's normal mode of operation differs on every one of these points, |
| 426 | with the specific designed-in goal of promoting SQLite's cathedral |
| 427 | development model: |
| 428 | |
| 429 | * <b>Personal engagement:</b> SQLite's developers know each |
| 430 | other by name and work together daily on the project. |
| 431 | |
| 432 | * <b>Trust over hierarchy:</b> SQLite's developers check |
| 433 | changes into their local repository, and these are immediately and |
| 434 | automatically synchronized up to the central repository; there is no |
| 435 | "[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator |
| 436 | and lieutenants]" hierarchy as with Linux kernel contributions. D. |
| 437 | Richard Hipp rarely overrides decisions made by those he has trusted |
| 438 | with commit access on his repositories. Fossil allows you to give |
| 439 | [./caps/admin-v-setup.md|some users] more power over what |
| 440 | they can do with the repository, but Fossil [./caps/index.md#ucap | |
| 441 | only loosely supports] the enforcement of a development organization's |
| 442 | social and power hierarchies. Fossil is a great fit for |
| 443 | [https://en.wikipedia.org/wiki/Flat_organization|flat |
| 444 | organizations]. |
| 445 | |
| 446 | * <b>No easy drive-by contributions:</b> Git |
| 447 | [https://www.git-scm.com/docs/git-request-pull|pull requests] offer |
| 448 | a low-friction path to accepting |
| 449 | [https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by |
| 450 | contributions]. Fossil's closest equivalents are its unique |
| 451 | [/help?cmd=bundle|bundle] and [/help?cmd=patch|patch] features, which require higher engagement |
| 452 | than firing off a PR.⁷ This difference comes directly from the |
| 453 | initial designed purpose for each tool: the SQLite project doesn't |
| 454 | accept outside contributions from previously-unknown developers, but |
| 455 | the Linux kernel does. |
| 456 | |
| 457 | * <b>No rebasing:</b> When your local repo clone syncs changes |
| 458 | up to its parent, those changes are sent exactly as they were |
| 459 | committed locally. [#history|There is no rebasing mechanism in |
| 460 | Fossil, on purpose.] |
| 461 | |
| 462 | * <b>Sync over push:</b> Explicit pushes are uncommon in |
| 463 | Fossil-based projects: the default is to rely on |
| 464 | [/help?cmd=autosync|autosync mode] instead, in which each commit |
| 465 | syncs immediately to its parent repository. This is a mode so you |
| 466 | can turn it off temporarily when needed, such as when working |
| 467 | offline. Fossil is still a truly distributed version control system; |
| 468 | it's just that its starting default is to assume you're rarely out |
| 469 | of communication with the parent repo. |
| 470 | <br><br> |
| 471 | This is not merely a reflection of modern always-connected computing |
| 472 | environments. It is a conscious decision in direct support of |
| 473 | SQLite's cathedral development model: we don't want developers going |
| 474 | dark, then showing up weeks later with a massive bolus of changes |
| 475 | for us to integrate all at once. |
| 476 | [https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy] |
| 477 | put it well in his book on software project management, |
| 478 | <i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software |
| 479 | Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware |
| 480 | of a guy in a room]." |
| 481 | |
| 482 | * <b>Branch names sync:</b> Unlike in Git, branch names in |
| 483 | Fossil are not purely local labels. They sync along with everything |
| 484 | else, so everyone sees the same set of branch names. Fossil's design |
| 485 | choice here is a direct reflection of the Linux vs. SQLite project |
| 486 | outlook: SQLite's developers collaborate closely on a single |
| 487 | coherent project, whereas Linux's developers go off on tangents and |
| 488 | occasionally send selected change sets to each other. |
| 489 | |
| 490 | * <b>Private branches are rare:</b> |
| 491 | [/doc/trunk/www/private.wiki|Private branches exist in Fossil], but |
| 492 | they're normally used to handle rare exception cases, whereas in |
| 493 | many Git projects, they're part of the straight-line development |
| 494 | process. |
| 495 | |
| 496 | * <b>Identical clones:</b> Fossil's autosync system tries to |
| 497 | keep each local clone identical to the repository it cloned |
| 498 | from. |
| 499 | |
| 500 | Where Git encourages siloed development, Fossil fights against it. |
| 501 | Fossil places a lot of emphasis on synchronizing everyone's work and on |
| 502 | reporting on the state of the project and the work of its developers, so |
| 503 | that everyone — especially the project leader — can maintain a better |
| 504 |
+168
-40
| --- www/gitusers.md | ||
| +++ www/gitusers.md | ||
| @@ -4,15 +4,19 @@ | ||
| 4 | 4 | |
| 5 | 5 | Fossil shares many similarities with Git. In many cases, the |
| 6 | 6 | sub-commands are identical: [`fossil bisect`][fbis] does essentially the |
| 7 | 7 | same thing as [`git bisect`][gbis], for example. |
| 8 | 8 | |
| 9 | -This document covers the cases where there is no simple 1:1 mapping, | |
| 9 | +Yet, Fossil is not merely Git with a bunch of commands misspelled. If | |
| 10 | +that were the case, we could give you a two-column translation table | |
| 11 | +which would tell you [how to say things like “`git reset --hard HEAD`”](#reset) in | |
| 12 | +this funny ol’ Fossil dialect of Git and be done. The purpose of this | |
| 13 | +document is to cover all the cases where there is no simple 1:1 mapping, | |
| 10 | 14 | usually because of intentional design differences in Fossil that prevent |
| 11 | 15 | it from working exactly like Git. We choose to explain these differences |
| 12 | -rather than provide a simple “translation dictionary,” since to | |
| 13 | -understand the conversion, you need to know why the difference exists. | |
| 16 | +since to understand the conversion, you need to know why each difference | |
| 17 | +exists. | |
| 14 | 18 | |
| 15 | 19 | We focus on practical command examples here, leaving discussions of the |
| 16 | 20 | philosophical underpinnings that drive these command differences to [another |
| 17 | 21 | document][fvg]. The [case studies](#cs1) do get a bit philosophical, but |
| 18 | 22 | it is with the aim of illustrating how these Fossil design differences |
| @@ -571,10 +575,40 @@ | ||
| 571 | 575 | indeed to any other Fossil command documented to accept a `VERSION` or |
| 572 | 576 | `NAME` string.) |
| 573 | 577 | |
| 574 | 578 | [scin]: ./checkin_names.wiki |
| 575 | 579 | |
| 580 | + | |
| 581 | +<a id="syncall"></a> | |
| 582 | +## Sync Is All-or-Nothing | |
| 583 | + | |
| 584 | +Fossil does not support the concept of syncing, pushing, or pulling | |
| 585 | +individual branches. When you sync/push/pull in Fossil, it | |
| 586 | +processes all artifacts in its hash tree: | |
| 587 | +branches, tags, wiki articles, tickets, forum posts, technotes… | |
| 588 | +This is [not quite “everything,” full stop][bu], but it’s close. | |
| 589 | +[Fossil is an AP-mode system][capt], which in this case means it works | |
| 590 | +*very hard* to ensure that all repos are as close to identical as it can | |
| 591 | +make them under this eventually-consistent design philosophy. | |
| 592 | + | |
| 593 | +Branch *names* sync automatically in Fossil, not just the | |
| 594 | +content of those branches. That means this common Git command: | |
| 595 | + | |
| 596 | + git push origin master | |
| 597 | + | |
| 598 | +…is simply this in Fossil: | |
| 599 | + | |
| 600 | + fossil push | |
| 601 | + | |
| 602 | +Fossil doesn’t need to be told what to push or where to push it: it just | |
| 603 | +keeps using the same remote server URL you gave it last | |
| 604 | +until you [tell it to do something different][rem]. It pushes all | |
| 605 | +branches, not just one named local branch. | |
| 606 | + | |
| 607 | +[capt]: ./cap-theorem.md | |
| 608 | +[rem]: /help?cmd=remote | |
| 609 | + | |
| 576 | 610 | |
| 577 | 611 | <a id="autosync"></a> |
| 578 | 612 | ## Autosync |
| 579 | 613 | |
| 580 | 614 | Fossil’s [autosync][wflow] feature, normally enabled, has no |
| @@ -581,26 +615,52 @@ | ||
| 581 | 615 | equivalent in Git. If you want Fossil to behave like Git, you can turn |
| 582 | 616 | it off: |
| 583 | 617 | |
| 584 | 618 | fossil set autosync 0 |
| 585 | 619 | |
| 586 | -However, it’s better to understand what the feature does and why it is enabled by | |
| 587 | -default. | |
| 588 | - | |
| 589 | -When autosync is enabled, Fossil automatically pushes your changes | |
| 590 | -to the remote server whenever you "`fossil commit`", and it | |
| 591 | -pulls all remote changes down to your local clone of the repository as | |
| 592 | -part of a "`fossil update`". | |
| 593 | -This provides most of the advantages of a centralized version control | |
| 594 | -system while retaining the advantages of distributed version control: | |
| 620 | +Let’s say that you have a typical server-and-workstations model with two | |
| 621 | +working clones on different machines, that you have disabled autosync, | |
| 622 | +and that this common sequence then occurs: | |
| 623 | + | |
| 624 | +1. Alice commits to her local clone and *separately* pushes the change | |
| 625 | + up to Condor — their central server — in typical Git fashion. | |
| 626 | +2. Bob does the same. | |
| 627 | +3. Alice brings Bob’s changes down from Condor with “`fossil pull`,” sees | |
| 628 | + what he did to their shared working branch, and becomes most wrathful. | |
| 629 | + (Tsk, tsk.) | |
| 630 | + | |
| 631 | +We’ll get to what you do about this situation [below](#reset), but for | |
| 632 | +now let us focus on the fact that disabling autosync makes it easier for | |
| 633 | +[forks] to occur in the development history. If all three machines had | |
| 634 | +been online and syncing at the time the sequence above began, Bob would | |
| 635 | +have been warned in step 2 that committing to the central repo would | |
| 636 | +create a fork and would be invited to fix it before committing. | |
| 637 | +Likewise, it would allow Fossil to warn Alice about the new | |
| 638 | +tip-of-branch commit the next time she triggers an implicit autosync at | |
| 639 | +step 3, giving her a chance to bring Bob’s changes down in a | |
| 640 | +non-conflicting manner, allowing work to proceed with minimal fuss. | |
| 641 | + | |
| 642 | +Fossil, being a distributed version control system, cannot guarantee | |
| 643 | +that sequence of events. Because it allows Alice’s work to proceed | |
| 644 | +asynchronously, it gives her the chance to create *another* inadvertent | |
| 645 | +fork before she can trigger an autosync. This is not a serious problem; | |
| 646 | +Fossil resolves it the same way as with Bob, by inviting her to fix this | |
| 647 | +second fork in the same manner as it did with Bob. It gets both parties | |
| 648 | +back onto a single track as expeditiously as possible by moving the | |
| 649 | +synchronization point out of the expensive human-time workflow and into | |
| 650 | +the software system, where it’s cheapest to resolve. | |
| 651 | + | |
| 652 | +Autosync provides Fossil with most of the advantages of a centralized | |
| 653 | +version control system while retaining the advantages of distributed | |
| 654 | +version control: | |
| 595 | 655 | |
| 596 | 656 | 1. Your work stays synced up with your coworkers’ efforts as long as your |
| 597 | 657 | machine can connect to the remote repository. At need, you can go |
| 598 | 658 | off-network and continue work atop the last version you synced with |
| 599 | 659 | the remote. |
| 600 | 660 | |
| 601 | -2. It provides immediate off-machine backup of your commits. Unlike | |
| 661 | +2. You get implicit off-machine backup of your commits. Unlike | |
| 602 | 662 | centralized version control, though, you can still work while |
| 603 | 663 | disconnected; your changes will sync up with the remote once you get |
| 604 | 664 | back online. |
| 605 | 665 | |
| 606 | 666 | 3. Because there is [little distinction][bu] between the clones in the Fossil |
| @@ -612,38 +672,106 @@ | ||
| 612 | 672 | remote. If the failed remote comes back later, it can sync with the |
| 613 | 673 | new central version, then perhaps take over as the primary source of |
| 614 | 674 | truth once again. |
| 615 | 675 | |
| 616 | 676 | [bu]: ./backup.md |
| 677 | +[forks]: ./branching.wiki | |
| 617 | 678 | [setup]: ./caps/admin-v-setup.md#apsu |
| 618 | 679 | [wflow]: ./concepts.wiki#workflow |
| 619 | 680 | |
| 620 | 681 | |
| 621 | -<a id="syncall"></a> | |
| 622 | -## Sync Is All-or-Nothing | |
| 623 | - | |
| 624 | -Fossil does not support the concept of syncing, pushing, or pulling | |
| 625 | -individual branches. When you sync/push/pull in Fossil, it | |
| 626 | -processes all artifacts in its hash tree: | |
| 627 | -branches, tags, wiki articles, tickets, forum posts, technotes… | |
| 628 | -This is [not quite “everything,” full stop][bu], but it’s close. | |
| 629 | - | |
| 630 | -Furthermore, branch *names* sync automatically in Fossil, not just the | |
| 631 | -content of those branches. That means this common Git command: | |
| 632 | - | |
| 633 | - git push origin master | |
| 634 | - | |
| 635 | -…is simply this in Fossil: | |
| 636 | - | |
| 637 | - fossil push | |
| 638 | - | |
| 639 | -Fossil doesn’t need to be told what to push or where to push it: it just | |
| 640 | -keeps using the same remote server URL you gave it last | |
| 641 | -until you [tell it to do something different][rem], and it pushes all | |
| 642 | -branches, not just one named local branch. | |
| 643 | - | |
| 644 | -[rem]: /help?cmd=remote | |
| 682 | +<a id="reset"></a> | |
| 683 | +## Resetting the Repository | |
| 684 | + | |
| 685 | +Extending from [the prior item](#syncall), you may correctly infer that | |
| 686 | +“[delete the project and download a fresh copy][x1597]” has no part in | |
| 687 | +the Fossil Way. Ideally, you should never find yourself forced into | |
| 688 | +desperate measures like this:(^Parsing the output of `fossil status` is | |
| 689 | +usually a mistake since it relies on a potentially unstable interface. | |
| 690 | +We make no guarantee that there will always be a line beginning with | |
| 691 | +“`repo`” and that it will be separated from the repository’s file name | |
| 692 | +by a colon. The simplified example above is also liable to become | |
| 693 | +confused by whitespace in file names.) | |
| 694 | + | |
| 695 | + | |
| 696 | +``` | |
| 697 | + $ repo=$(fossil status | grep ^repo | cut -f2 -d:) | |
| 698 | + $ url=$(fossil remote) | |
| 699 | + $ fossil close # Stop here and think if it warns you! | |
| 700 | + $ mv $repo ${repo}.old | |
| 701 | + $ fossil clone $url $repo | |
| 702 | + $ fossil open --force $repo | |
| 703 | +``` | |
| 704 | + | |
| 705 | +What, then, should you as a Git transplant do instead when you find | |
| 706 | +yourself reaching for “`git reset`”? | |
| 707 | + | |
| 708 | +Since the correct answer to that depends on why you think it’s a good | |
| 709 | +solution to your immediate problem, we’ll take our motivating scenario | |
| 710 | +from the problem setup above, where we discussed Fossil’s [autosync] | |
| 711 | +feature. Let us further say Alice’s pique results from a belief that | |
| 712 | +Bob’s commit is objectively wrong-headed and should be expunged | |
| 713 | +henceforth. Since Fossil goes out of its way to ensure that [commits are | |
| 714 | +durable][wdm], it should be no further surprise that there is no easier | |
| 715 | +method to reset Bob’s clone in favor of Alice’s than the above sequence | |
| 716 | +in Fossil’s command set. Except in extreme situations, we believe that | |
| 717 | +sort of thing is unnecessary. | |
| 718 | + | |
| 719 | +Instead, Bob can say something like this: | |
| 720 | + | |
| 721 | +``` | |
| 722 | + fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip | |
| 723 | + fossil up trunk | |
| 724 | + fossil push | |
| 725 | +``` | |
| 726 | + | |
| 727 | +Unlike in Git, the “`amend`” command doesn’t modify prior committed | |
| 728 | +artifacts. Bob’s first command doesn’t delete anything, merely tells | |
| 729 | +Fossil to hide his mistake from timeline views by inserting a few new | |
| 730 | +records into the local repository to change how the client interprets | |
| 731 | +the data it finds there henceforth.(^One to change the tag marking this | |
| 732 | +commit’s branch name to “`MISTAKE`,” one to mark that branch as hidden, | |
| 733 | +and one to close it to further commits.). | |
| 734 | + | |
| 735 | +Bob’s second command switches his working directory back to the prior | |
| 736 | +commit on that branch. We’re presuming it was “`trunk`” for the sake of | |
| 737 | +the example, but it works for any parent branch name. The command works | |
| 738 | +because the name “`trunk`” now means something different to Fossil by | |
| 739 | +virtue of the first command. | |
| 740 | + | |
| 741 | +Bob’s third command pushes the changes up to the central machine to | |
| 742 | +inform everyone else of his amendment.(^Amendments don’t autosync in | |
| 743 | +Fossil because they don’t change any previous commits, allowing the | |
| 744 | +other clones to continue working safely with their existing commit | |
| 745 | +hashes.) | |
| 746 | + | |
| 747 | +In this scheme, Alice then needs to say “`fossil update trunk`” in order | |
| 748 | +to return her check-out’s parent commit to the previous version lest her | |
| 749 | +next attempted commit land atop this mistake branch. The fact that Bob | |
| 750 | +marked the branch as closed will prevent that from going thru, cluing | |
| 751 | +Alice into what she needs to do to remedy the situation, but that merely | |
| 752 | +shows why it’s a better workflow if Alice makes the amendment herself: | |
| 753 | + | |
| 754 | +``` | |
| 755 | + fossil amend --branch MISTAKE --hide --close \ | |
| 756 | + -m "shunt Bob’s erroneous commit off" tip | |
| 757 | + fossil up trunk | |
| 758 | + fossil push | |
| 759 | +``` | |
| 760 | + | |
| 761 | +Then she can fire off an email listing Bob’s assorted failings and go | |
| 762 | +about her work. This asynchronous workflow solves the problem without | |
| 763 | +requiring explicit coordination with Bob. When he gets his email, he can | |
| 764 | +then say “`fossil up trunk`” himself, which by default will trigger an | |
| 765 | +autosync, pulling down Alice’s amendments and getting him back onto her | |
| 766 | +development track. | |
| 767 | + | |
| 768 | +Remember that [branch names need not be unique](#btnames) in Fossil. You | |
| 769 | +are free to reuse this “`MISTAKE`” branch name as often as you need to. | |
| 770 | + | |
| 771 | +[autosync]: #autosync | |
| 772 | +[x1597]: https://xkcd.com/1597/ | |
| 645 | 773 | |
| 646 | 774 | |
| 647 | 775 | <a id="trunk"></a> |
| 648 | 776 | ## The Main Branch Is Called "`trunk`" |
| 649 | 777 | |
| @@ -951,11 +1079,11 @@ | ||
| 951 | 1079 | We believe you get such answers to Git help requests in part |
| 952 | 1080 | because of its lack of an always-up-to-date [index into its log](#log) and in |
| 953 | 1081 | part because of its “small tools loosely joined” design philosophy. This |
| 954 | 1082 | sort of command is therefore composed piece by piece: |
| 955 | 1083 | |
| 956 | -<center>◆ ◆ ◆</center> | |
| 1084 | +<p style="text-align:center">◆ ◆ ◆</p> | |
| 957 | 1085 | |
| 958 | 1086 | “Oh, I know, I’ll search the rev-list, which outputs commit IDs by |
| 959 | 1087 | parsing the log backwards from `HEAD`! Easy!” |
| 960 | 1088 | |
| 961 | 1089 | git rev-list --before=2020-03-17 |
| @@ -985,11 +1113,11 @@ | ||
| 985 | 1113 | |
| 986 | 1114 | git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) |
| 987 | 1115 | |
| 988 | 1116 | “Success, I guess?” |
| 989 | 1117 | |
| 990 | -<center>◆ ◆ ◆</center> | |
| 1118 | +<p style="text-align:center">◆ ◆ ◆</p> | |
| 991 | 1119 | |
| 992 | 1120 | This vignette is meant to explain some of Git’s popularity: it rewards |
| 993 | 1121 | the sort of people who enjoy puzzles, many of whom are software |
| 994 | 1122 | developers and thus need a tool like Git. Too bad if you’re just a |
| 995 | 1123 | normal user. |
| @@ -1185,11 +1313,11 @@ | ||
| 1185 | 1313 | |
| 1186 | 1314 | fossil ci |
| 1187 | 1315 | |
| 1188 | 1316 | It’s one short command for Fossil instead of three for Git — or two if |
| 1189 | 1317 | you abbreviate it as “`git commit -a && git push`” — because of Fossil’s |
| 1190 | -[autosync feature](#autosync) feature and deliberate omission of a | |
| 1318 | +[autosync] feature and deliberate omission of a | |
| 1191 | 1319 | [staging feature](#staging). |
| 1192 | 1320 | |
| 1193 | 1321 | The “Friday afternoon sync-up” case is simpler, too: |
| 1194 | 1322 | |
| 1195 | 1323 | fossil remote work |
| 1196 | 1324 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -4,15 +4,19 @@ | |
| 4 | |
| 5 | Fossil shares many similarities with Git. In many cases, the |
| 6 | sub-commands are identical: [`fossil bisect`][fbis] does essentially the |
| 7 | same thing as [`git bisect`][gbis], for example. |
| 8 | |
| 9 | This document covers the cases where there is no simple 1:1 mapping, |
| 10 | usually because of intentional design differences in Fossil that prevent |
| 11 | it from working exactly like Git. We choose to explain these differences |
| 12 | rather than provide a simple “translation dictionary,” since to |
| 13 | understand the conversion, you need to know why the difference exists. |
| 14 | |
| 15 | We focus on practical command examples here, leaving discussions of the |
| 16 | philosophical underpinnings that drive these command differences to [another |
| 17 | document][fvg]. The [case studies](#cs1) do get a bit philosophical, but |
| 18 | it is with the aim of illustrating how these Fossil design differences |
| @@ -571,10 +575,40 @@ | |
| 571 | indeed to any other Fossil command documented to accept a `VERSION` or |
| 572 | `NAME` string.) |
| 573 | |
| 574 | [scin]: ./checkin_names.wiki |
| 575 | |
| 576 | |
| 577 | <a id="autosync"></a> |
| 578 | ## Autosync |
| 579 | |
| 580 | Fossil’s [autosync][wflow] feature, normally enabled, has no |
| @@ -581,26 +615,52 @@ | |
| 581 | equivalent in Git. If you want Fossil to behave like Git, you can turn |
| 582 | it off: |
| 583 | |
| 584 | fossil set autosync 0 |
| 585 | |
| 586 | However, it’s better to understand what the feature does and why it is enabled by |
| 587 | default. |
| 588 | |
| 589 | When autosync is enabled, Fossil automatically pushes your changes |
| 590 | to the remote server whenever you "`fossil commit`", and it |
| 591 | pulls all remote changes down to your local clone of the repository as |
| 592 | part of a "`fossil update`". |
| 593 | This provides most of the advantages of a centralized version control |
| 594 | system while retaining the advantages of distributed version control: |
| 595 | |
| 596 | 1. Your work stays synced up with your coworkers’ efforts as long as your |
| 597 | machine can connect to the remote repository. At need, you can go |
| 598 | off-network and continue work atop the last version you synced with |
| 599 | the remote. |
| 600 | |
| 601 | 2. It provides immediate off-machine backup of your commits. Unlike |
| 602 | centralized version control, though, you can still work while |
| 603 | disconnected; your changes will sync up with the remote once you get |
| 604 | back online. |
| 605 | |
| 606 | 3. Because there is [little distinction][bu] between the clones in the Fossil |
| @@ -612,38 +672,106 @@ | |
| 612 | remote. If the failed remote comes back later, it can sync with the |
| 613 | new central version, then perhaps take over as the primary source of |
| 614 | truth once again. |
| 615 | |
| 616 | [bu]: ./backup.md |
| 617 | [setup]: ./caps/admin-v-setup.md#apsu |
| 618 | [wflow]: ./concepts.wiki#workflow |
| 619 | |
| 620 | |
| 621 | <a id="syncall"></a> |
| 622 | ## Sync Is All-or-Nothing |
| 623 | |
| 624 | Fossil does not support the concept of syncing, pushing, or pulling |
| 625 | individual branches. When you sync/push/pull in Fossil, it |
| 626 | processes all artifacts in its hash tree: |
| 627 | branches, tags, wiki articles, tickets, forum posts, technotes… |
| 628 | This is [not quite “everything,” full stop][bu], but it’s close. |
| 629 | |
| 630 | Furthermore, branch *names* sync automatically in Fossil, not just the |
| 631 | content of those branches. That means this common Git command: |
| 632 | |
| 633 | git push origin master |
| 634 | |
| 635 | …is simply this in Fossil: |
| 636 | |
| 637 | fossil push |
| 638 | |
| 639 | Fossil doesn’t need to be told what to push or where to push it: it just |
| 640 | keeps using the same remote server URL you gave it last |
| 641 | until you [tell it to do something different][rem], and it pushes all |
| 642 | branches, not just one named local branch. |
| 643 | |
| 644 | [rem]: /help?cmd=remote |
| 645 | |
| 646 | |
| 647 | <a id="trunk"></a> |
| 648 | ## The Main Branch Is Called "`trunk`" |
| 649 | |
| @@ -951,11 +1079,11 @@ | |
| 951 | We believe you get such answers to Git help requests in part |
| 952 | because of its lack of an always-up-to-date [index into its log](#log) and in |
| 953 | part because of its “small tools loosely joined” design philosophy. This |
| 954 | sort of command is therefore composed piece by piece: |
| 955 | |
| 956 | <center>◆ ◆ ◆</center> |
| 957 | |
| 958 | “Oh, I know, I’ll search the rev-list, which outputs commit IDs by |
| 959 | parsing the log backwards from `HEAD`! Easy!” |
| 960 | |
| 961 | git rev-list --before=2020-03-17 |
| @@ -985,11 +1113,11 @@ | |
| 985 | |
| 986 | git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) |
| 987 | |
| 988 | “Success, I guess?” |
| 989 | |
| 990 | <center>◆ ◆ ◆</center> |
| 991 | |
| 992 | This vignette is meant to explain some of Git’s popularity: it rewards |
| 993 | the sort of people who enjoy puzzles, many of whom are software |
| 994 | developers and thus need a tool like Git. Too bad if you’re just a |
| 995 | normal user. |
| @@ -1185,11 +1313,11 @@ | |
| 1185 | |
| 1186 | fossil ci |
| 1187 | |
| 1188 | It’s one short command for Fossil instead of three for Git — or two if |
| 1189 | you abbreviate it as “`git commit -a && git push`” — because of Fossil’s |
| 1190 | [autosync feature](#autosync) feature and deliberate omission of a |
| 1191 | [staging feature](#staging). |
| 1192 | |
| 1193 | The “Friday afternoon sync-up” case is simpler, too: |
| 1194 | |
| 1195 | fossil remote work |
| 1196 |
| --- www/gitusers.md | |
| +++ www/gitusers.md | |
| @@ -4,15 +4,19 @@ | |
| 4 | |
| 5 | Fossil shares many similarities with Git. In many cases, the |
| 6 | sub-commands are identical: [`fossil bisect`][fbis] does essentially the |
| 7 | same thing as [`git bisect`][gbis], for example. |
| 8 | |
| 9 | Yet, Fossil is not merely Git with a bunch of commands misspelled. If |
| 10 | that were the case, we could give you a two-column translation table |
| 11 | which would tell you [how to say things like “`git reset --hard HEAD`”](#reset) in |
| 12 | this funny ol’ Fossil dialect of Git and be done. The purpose of this |
| 13 | document is to cover all the cases where there is no simple 1:1 mapping, |
| 14 | usually because of intentional design differences in Fossil that prevent |
| 15 | it from working exactly like Git. We choose to explain these differences |
| 16 | since to understand the conversion, you need to know why each difference |
| 17 | exists. |
| 18 | |
| 19 | We focus on practical command examples here, leaving discussions of the |
| 20 | philosophical underpinnings that drive these command differences to [another |
| 21 | document][fvg]. The [case studies](#cs1) do get a bit philosophical, but |
| 22 | it is with the aim of illustrating how these Fossil design differences |
| @@ -571,10 +575,40 @@ | |
| 575 | indeed to any other Fossil command documented to accept a `VERSION` or |
| 576 | `NAME` string.) |
| 577 | |
| 578 | [scin]: ./checkin_names.wiki |
| 579 | |
| 580 | |
| 581 | <a id="syncall"></a> |
| 582 | ## Sync Is All-or-Nothing |
| 583 | |
| 584 | Fossil does not support the concept of syncing, pushing, or pulling |
| 585 | individual branches. When you sync/push/pull in Fossil, it |
| 586 | processes all artifacts in its hash tree: |
| 587 | branches, tags, wiki articles, tickets, forum posts, technotes… |
| 588 | This is [not quite “everything,” full stop][bu], but it’s close. |
| 589 | [Fossil is an AP-mode system][capt], which in this case means it works |
| 590 | *very hard* to ensure that all repos are as close to identical as it can |
| 591 | make them under this eventually-consistent design philosophy. |
| 592 | |
| 593 | Branch *names* sync automatically in Fossil, not just the |
| 594 | content of those branches. That means this common Git command: |
| 595 | |
| 596 | git push origin master |
| 597 | |
| 598 | …is simply this in Fossil: |
| 599 | |
| 600 | fossil push |
| 601 | |
| 602 | Fossil doesn’t need to be told what to push or where to push it: it just |
| 603 | keeps using the same remote server URL you gave it last |
| 604 | until you [tell it to do something different][rem]. It pushes all |
| 605 | branches, not just one named local branch. |
| 606 | |
| 607 | [capt]: ./cap-theorem.md |
| 608 | [rem]: /help?cmd=remote |
| 609 | |
| 610 | |
| 611 | <a id="autosync"></a> |
| 612 | ## Autosync |
| 613 | |
| 614 | Fossil’s [autosync][wflow] feature, normally enabled, has no |
| @@ -581,26 +615,52 @@ | |
| 615 | equivalent in Git. If you want Fossil to behave like Git, you can turn |
| 616 | it off: |
| 617 | |
| 618 | fossil set autosync 0 |
| 619 | |
| 620 | Let’s say that you have a typical server-and-workstations model with two |
| 621 | working clones on different machines, that you have disabled autosync, |
| 622 | and that this common sequence then occurs: |
| 623 | |
| 624 | 1. Alice commits to her local clone and *separately* pushes the change |
| 625 | up to Condor — their central server — in typical Git fashion. |
| 626 | 2. Bob does the same. |
| 627 | 3. Alice brings Bob’s changes down from Condor with “`fossil pull`,” sees |
| 628 | what he did to their shared working branch, and becomes most wrathful. |
| 629 | (Tsk, tsk.) |
| 630 | |
| 631 | We’ll get to what you do about this situation [below](#reset), but for |
| 632 | now let us focus on the fact that disabling autosync makes it easier for |
| 633 | [forks] to occur in the development history. If all three machines had |
| 634 | been online and syncing at the time the sequence above began, Bob would |
| 635 | have been warned in step 2 that committing to the central repo would |
| 636 | create a fork and would be invited to fix it before committing. |
| 637 | Likewise, it would allow Fossil to warn Alice about the new |
| 638 | tip-of-branch commit the next time she triggers an implicit autosync at |
| 639 | step 3, giving her a chance to bring Bob’s changes down in a |
| 640 | non-conflicting manner, allowing work to proceed with minimal fuss. |
| 641 | |
| 642 | Fossil, being a distributed version control system, cannot guarantee |
| 643 | that sequence of events. Because it allows Alice’s work to proceed |
| 644 | asynchronously, it gives her the chance to create *another* inadvertent |
| 645 | fork before she can trigger an autosync. This is not a serious problem; |
| 646 | Fossil resolves it the same way as with Bob, by inviting her to fix this |
| 647 | second fork in the same manner as it did with Bob. It gets both parties |
| 648 | back onto a single track as expeditiously as possible by moving the |
| 649 | synchronization point out of the expensive human-time workflow and into |
| 650 | the software system, where it’s cheapest to resolve. |
| 651 | |
| 652 | Autosync provides Fossil with most of the advantages of a centralized |
| 653 | version control system while retaining the advantages of distributed |
| 654 | version control: |
| 655 | |
| 656 | 1. Your work stays synced up with your coworkers’ efforts as long as your |
| 657 | machine can connect to the remote repository. At need, you can go |
| 658 | off-network and continue work atop the last version you synced with |
| 659 | the remote. |
| 660 | |
| 661 | 2. You get implicit off-machine backup of your commits. Unlike |
| 662 | centralized version control, though, you can still work while |
| 663 | disconnected; your changes will sync up with the remote once you get |
| 664 | back online. |
| 665 | |
| 666 | 3. Because there is [little distinction][bu] between the clones in the Fossil |
| @@ -612,38 +672,106 @@ | |
| 672 | remote. If the failed remote comes back later, it can sync with the |
| 673 | new central version, then perhaps take over as the primary source of |
| 674 | truth once again. |
| 675 | |
| 676 | [bu]: ./backup.md |
| 677 | [forks]: ./branching.wiki |
| 678 | [setup]: ./caps/admin-v-setup.md#apsu |
| 679 | [wflow]: ./concepts.wiki#workflow |
| 680 | |
| 681 | |
| 682 | <a id="reset"></a> |
| 683 | ## Resetting the Repository |
| 684 | |
| 685 | Extending from [the prior item](#syncall), you may correctly infer that |
| 686 | “[delete the project and download a fresh copy][x1597]” has no part in |
| 687 | the Fossil Way. Ideally, you should never find yourself forced into |
| 688 | desperate measures like this:(^Parsing the output of `fossil status` is |
| 689 | usually a mistake since it relies on a potentially unstable interface. |
| 690 | We make no guarantee that there will always be a line beginning with |
| 691 | “`repo`” and that it will be separated from the repository’s file name |
| 692 | by a colon. The simplified example above is also liable to become |
| 693 | confused by whitespace in file names.) |
| 694 | |
| 695 | |
| 696 | ``` |
| 697 | $ repo=$(fossil status | grep ^repo | cut -f2 -d:) |
| 698 | $ url=$(fossil remote) |
| 699 | $ fossil close # Stop here and think if it warns you! |
| 700 | $ mv $repo ${repo}.old |
| 701 | $ fossil clone $url $repo |
| 702 | $ fossil open --force $repo |
| 703 | ``` |
| 704 | |
| 705 | What, then, should you as a Git transplant do instead when you find |
| 706 | yourself reaching for “`git reset`”? |
| 707 | |
| 708 | Since the correct answer to that depends on why you think it’s a good |
| 709 | solution to your immediate problem, we’ll take our motivating scenario |
| 710 | from the problem setup above, where we discussed Fossil’s [autosync] |
| 711 | feature. Let us further say Alice’s pique results from a belief that |
| 712 | Bob’s commit is objectively wrong-headed and should be expunged |
| 713 | henceforth. Since Fossil goes out of its way to ensure that [commits are |
| 714 | durable][wdm], it should be no further surprise that there is no easier |
| 715 | method to reset Bob’s clone in favor of Alice’s than the above sequence |
| 716 | in Fossil’s command set. Except in extreme situations, we believe that |
| 717 | sort of thing is unnecessary. |
| 718 | |
| 719 | Instead, Bob can say something like this: |
| 720 | |
| 721 | ``` |
| 722 | fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip |
| 723 | fossil up trunk |
| 724 | fossil push |
| 725 | ``` |
| 726 | |
| 727 | Unlike in Git, the “`amend`” command doesn’t modify prior committed |
| 728 | artifacts. Bob’s first command doesn’t delete anything, merely tells |
| 729 | Fossil to hide his mistake from timeline views by inserting a few new |
| 730 | records into the local repository to change how the client interprets |
| 731 | the data it finds there henceforth.(^One to change the tag marking this |
| 732 | commit’s branch name to “`MISTAKE`,” one to mark that branch as hidden, |
| 733 | and one to close it to further commits.). |
| 734 | |
| 735 | Bob’s second command switches his working directory back to the prior |
| 736 | commit on that branch. We’re presuming it was “`trunk`” for the sake of |
| 737 | the example, but it works for any parent branch name. The command works |
| 738 | because the name “`trunk`” now means something different to Fossil by |
| 739 | virtue of the first command. |
| 740 | |
| 741 | Bob’s third command pushes the changes up to the central machine to |
| 742 | inform everyone else of his amendment.(^Amendments don’t autosync in |
| 743 | Fossil because they don’t change any previous commits, allowing the |
| 744 | other clones to continue working safely with their existing commit |
| 745 | hashes.) |
| 746 | |
| 747 | In this scheme, Alice then needs to say “`fossil update trunk`” in order |
| 748 | to return her check-out’s parent commit to the previous version lest her |
| 749 | next attempted commit land atop this mistake branch. The fact that Bob |
| 750 | marked the branch as closed will prevent that from going thru, cluing |
| 751 | Alice into what she needs to do to remedy the situation, but that merely |
| 752 | shows why it’s a better workflow if Alice makes the amendment herself: |
| 753 | |
| 754 | ``` |
| 755 | fossil amend --branch MISTAKE --hide --close \ |
| 756 | -m "shunt Bob’s erroneous commit off" tip |
| 757 | fossil up trunk |
| 758 | fossil push |
| 759 | ``` |
| 760 | |
| 761 | Then she can fire off an email listing Bob’s assorted failings and go |
| 762 | about her work. This asynchronous workflow solves the problem without |
| 763 | requiring explicit coordination with Bob. When he gets his email, he can |
| 764 | then say “`fossil up trunk`” himself, which by default will trigger an |
| 765 | autosync, pulling down Alice’s amendments and getting him back onto her |
| 766 | development track. |
| 767 | |
| 768 | Remember that [branch names need not be unique](#btnames) in Fossil. You |
| 769 | are free to reuse this “`MISTAKE`” branch name as often as you need to. |
| 770 | |
| 771 | [autosync]: #autosync |
| 772 | [x1597]: https://xkcd.com/1597/ |
| 773 | |
| 774 | |
| 775 | <a id="trunk"></a> |
| 776 | ## The Main Branch Is Called "`trunk`" |
| 777 | |
| @@ -951,11 +1079,11 @@ | |
| 1079 | We believe you get such answers to Git help requests in part |
| 1080 | because of its lack of an always-up-to-date [index into its log](#log) and in |
| 1081 | part because of its “small tools loosely joined” design philosophy. This |
| 1082 | sort of command is therefore composed piece by piece: |
| 1083 | |
| 1084 | <p style="text-align:center">◆ ◆ ◆</p> |
| 1085 | |
| 1086 | “Oh, I know, I’ll search the rev-list, which outputs commit IDs by |
| 1087 | parsing the log backwards from `HEAD`! Easy!” |
| 1088 | |
| 1089 | git rev-list --before=2020-03-17 |
| @@ -985,11 +1113,11 @@ | |
| 1113 | |
| 1114 | git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master) |
| 1115 | |
| 1116 | “Success, I guess?” |
| 1117 | |
| 1118 | <p style="text-align:center">◆ ◆ ◆</p> |
| 1119 | |
| 1120 | This vignette is meant to explain some of Git’s popularity: it rewards |
| 1121 | the sort of people who enjoy puzzles, many of whom are software |
| 1122 | developers and thus need a tool like Git. Too bad if you’re just a |
| 1123 | normal user. |
| @@ -1185,11 +1313,11 @@ | |
| 1313 | |
| 1314 | fossil ci |
| 1315 | |
| 1316 | It’s one short command for Fossil instead of three for Git — or two if |
| 1317 | you abbreviate it as “`git commit -a && git push`” — because of Fossil’s |
| 1318 | [autosync] feature and deliberate omission of a |
| 1319 | [staging feature](#staging). |
| 1320 | |
| 1321 | The “Friday afternoon sync-up” case is simpler, too: |
| 1322 | |
| 1323 | fossil remote work |
| 1324 |
+63
-46
| --- www/globs.md | ||
| +++ www/globs.md | ||
| @@ -1,50 +1,60 @@ | ||
| 1 | 1 | # File Name Glob Patterns |
| 2 | 2 | |
| 3 | - | |
| 4 | 3 | A [glob pattern][glob] is a text expression that matches one or more |
| 5 | -file names using wild cards familiar to most users of a command line. | |
| 6 | -For example, `*` is a glob that matches any name at all and | |
| 4 | +file names using wildcards familiar to most users of a command line. | |
| 5 | +For example, `*` is a glob that matches any name at all, and | |
| 7 | 6 | `Readme.txt` is a glob that matches exactly one file. For purposes of |
| 8 | -Fossil's globs, a file name with a directory prefix is "just a string" | |
| 7 | +Fossil's globs, a complete path name is just a string, | |
| 9 | 8 | and the globs do not apply any special meaning to the directory part |
| 10 | -of the name. Thus the glob `*` matches any name, including any | |
| 9 | +of the name. Thus, the glob `*` matches any name, including any | |
| 11 | 10 | directory prefix, and `*/*` matches a name with _one or more_ |
| 12 | 11 | directory components. |
| 13 | 12 | |
| 14 | -A glob should not be confused with a [regular expression][regexp] (RE), | |
| 13 | +A glob should not be confused with a [regular expression][regexp] (RE) | |
| 15 | 14 | even though they use some of the same special characters for similar |
| 16 | -purposes, because [they are not fully compatible][greinc] pattern | |
| 15 | +purposes. [They are not fully compatible][greinc] pattern | |
| 17 | 16 | matching languages. Fossil uses globs when matching file names with the |
| 18 | 17 | settings described in this document, not REs. |
| 19 | 18 | |
| 20 | 19 | [glob]: https://en.wikipedia.org/wiki/Glob_(programming) |
| 21 | 20 | [greinc]: https://unix.stackexchange.com/a/57958/138 |
| 22 | 21 | [regexp]: https://en.wikipedia.org/wiki/Regular_expression |
| 23 | 22 | |
| 24 | -These settings hold one or more file glob patterns to cause Fossil to | |
| 23 | +[Fossil’s `*-glob` settings](#settings) hold one or more patterns to cause Fossil to | |
| 25 | 24 | give matching named files special treatment. Glob patterns are also |
| 26 | 25 | accepted in options to certain commands and as query parameters to |
| 27 | -certain Fossil UI web pages. | |
| 26 | +certain Fossil UI web pages. For consistency, settings such as | |
| 27 | +`empty-dirs` are parsed as a glob even though they aren’t then *applied* | |
| 28 | +as a glob since it allows [the same syntax rules](#syntax) to apply. | |
| 28 | 29 | |
| 29 | 30 | Where Fossil also accepts globs in commands, this handling may interact |
| 30 | 31 | with your OS’s command shell or its C runtime system, because they may |
| 31 | 32 | have their own glob pattern handling. We will detail such interactions |
| 32 | 33 | below. |
| 33 | 34 | |
| 34 | 35 | |
| 35 | -## Syntax | |
| 36 | +## <a id="syntax"></a>Syntax | |
| 36 | 37 | |
| 37 | 38 | Where Fossil accepts glob patterns, it will usually accept a *list* of |
| 38 | -such patterns, each individual pattern separated from the others | |
| 39 | -by white space or commas. If a glob must contain white spaces or | |
| 40 | -commas, it can be quoted with either single or double quotation marks. | |
| 41 | -A list is said to match if any one glob in the list | |
| 42 | -matches. | |
| 43 | - | |
| 44 | -A glob pattern matches a given file name if it successfully consumes and | |
| 45 | -matches the *entire* name. Partial matches are failed matches. | |
| 39 | +individual patterns separated from the others by whitespace or commas. | |
| 40 | + | |
| 41 | +The parser allows whitespace and commas in a pattern by quoting _the | |
| 42 | +entire pattern_ with either single or double quotation marks. Internal | |
| 43 | +quotation marks are treated literally. Moreover, a pattern that begins | |
| 44 | +with a quote mark ends when the first instance of the same mark occurs, | |
| 45 | +_not_ at a whitespace or comma. Thus, this: | |
| 46 | + | |
| 47 | + "foo bar"qux | |
| 48 | + | |
| 49 | +…constitutes _two_ patterns rather than one with an embedded space, in | |
| 50 | +contravention of normal shell quoting rules. | |
| 51 | + | |
| 52 | +A list matches a file when any pattern in that list matches. | |
| 53 | + | |
| 54 | +A pattern must consume and | |
| 55 | +match the *entire* file name to succeed. Partial matches are failed matches. | |
| 46 | 56 | |
| 47 | 57 | Most characters in a glob pattern consume a single character of the file |
| 48 | 58 | name and must match it exactly. For instance, “a” in a glob simply |
| 49 | 59 | matches the letter “a” in the file name unless it is inside a special |
| 50 | 60 | character sequence. |
| @@ -61,11 +71,11 @@ | ||
| 61 | 71 | |
| 62 | 72 | Note that unlike [POSIX globs][pg], these special characters and |
| 63 | 73 | sequences are allowed to match `/` directory separators as well as the |
| 64 | 74 | initial `.` in the name of a hidden file or directory. This is because |
| 65 | 75 | Fossil file names are stored as complete path names. The distinction |
| 66 | -between file name and directory name is “below” Fossil in this sense. | |
| 76 | +between file name and directory name is “underneath” Fossil in this sense. | |
| 67 | 77 | |
| 68 | 78 | [pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 |
| 69 | 79 | |
| 70 | 80 | The bracket expressions above require some additional explanation: |
| 71 | 81 | |
| @@ -163,14 +173,15 @@ | ||
| 163 | 173 | `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. |
| 164 | 174 | `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. |
| 165 | 175 | `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. |
| 166 | 176 | `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. |
| 167 | 177 | `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. |
| 178 | + | |
| 168 | 179 | |
| 169 | 180 | ## Where Globs are Used |
| 170 | 181 | |
| 171 | -### Settings that are Globs | |
| 182 | +### <a id="settings"></a>Settings that are Globs | |
| 172 | 183 | |
| 173 | 184 | These settings are all lists of glob patterns: |
| 174 | 185 | |
| 175 | 186 | :Setting |:Description |
| 176 | 187 | -------------------------------------------------------------------------------- |
| @@ -197,12 +208,16 @@ | ||
| 197 | 208 | commands. This is especially true when one (or more) IDEs are used in |
| 198 | 209 | a project because each IDE has its own ideas of how and where to cache |
| 199 | 210 | information that speeds up its browsing and building tasks but which |
| 200 | 211 | need not be preserved in your project's history. |
| 201 | 212 | |
| 213 | +Although the `empty-dirs` setting is not a list of glob patterns as | |
| 214 | +such, it is *parsed* that way for consistency among the settings, | |
| 215 | +allowing [the list parsing rules above](#syntax) to apply. | |
| 202 | 216 | |
| 203 | -### Commands that Refer to Globs | |
| 217 | + | |
| 218 | +### <a id="commands"></a>Commands that Refer to Globs | |
| 204 | 219 | |
| 205 | 220 | Many of the commands that respect the settings containing globs have |
| 206 | 221 | options to override some or all of the settings. These options are |
| 207 | 222 | usually named to correspond to the setting they override, such as |
| 208 | 223 | `--ignore` to override the `ignore-glob` setting. These commands are: |
| @@ -227,29 +242,29 @@ | ||
| 227 | 242 | The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that |
| 228 | 243 | implement or support with web servers provide a mechanism to name some |
| 229 | 244 | files to serve with static content where a list of glob patterns |
| 230 | 245 | specifies what content may be served. |
| 231 | 246 | |
| 232 | -[`add`]: /help?cmd=add | |
| 247 | +[`add`]: /help?cmd=add | |
| 233 | 248 | [`addremove`]: /help?cmd=addremove |
| 234 | -[`changes`]: /help?cmd=changes | |
| 235 | -[`clean`]: /help?cmd=clean | |
| 236 | -[`commit`]: /help?cmd=commit | |
| 237 | -[`extras`]: /help?cmd=extras | |
| 238 | -[`merge`]: /help?cmd=merge | |
| 239 | -[`settings`]: /help?cmd=settings | |
| 240 | -[`status`]: /help?cmd=status | |
| 241 | -[`touch`]: /help?cmd=touch | |
| 242 | -[`unset`]: /help?cmd=unset | |
| 243 | - | |
| 244 | -[`tarball`]: /help?cmd=tarball | |
| 245 | -[`zip`]: /help?cmd=zip | |
| 246 | - | |
| 247 | -[`http`]: /help?cmd=http | |
| 248 | -[`cgi`]: /help?cmd=cgi | |
| 249 | -[`server`]: /help?cmd=server | |
| 250 | -[`ui`]: /help?cmd=ui | |
| 249 | +[`changes`]: /help?cmd=changes | |
| 250 | +[`clean`]: /help?cmd=clean | |
| 251 | +[`commit`]: /help?cmd=commit | |
| 252 | +[`extras`]: /help?cmd=extras | |
| 253 | +[`merge`]: /help?cmd=merge | |
| 254 | +[`settings`]: /help?cmd=settings | |
| 255 | +[`status`]: /help?cmd=status | |
| 256 | +[`touch`]: /help?cmd=touch | |
| 257 | +[`unset`]: /help?cmd=unset | |
| 258 | + | |
| 259 | +[`tarball`]: /help?cmd=tarball | |
| 260 | +[`zip`]: /help?cmd=zip | |
| 261 | + | |
| 262 | +[`http`]: /help?cmd=http | |
| 263 | +[`cgi`]: /help?cmd=cgi | |
| 264 | +[`server`]: /help?cmd=server | |
| 265 | +[`ui`]: /help?cmd=ui | |
| 251 | 266 | |
| 252 | 267 | |
| 253 | 268 | ### Web Pages that Refer to Globs |
| 254 | 269 | |
| 255 | 270 | The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that |
| @@ -263,19 +278,19 @@ | ||
| 263 | 278 | of a specific checkin. They may be further restricted by query |
| 264 | 279 | parameters that specify glob patterns that name files to include or |
| 265 | 280 | exclude rather than taking the entire checkin. |
| 266 | 281 | |
| 267 | 282 | [`/timeline`]: /help?cmd=/timeline |
| 268 | -[`/tarball`]: /help?cmd=/tarball | |
| 269 | -[`/zip`]: /help?cmd=/zip | |
| 283 | +[`/tarball`]: /help?cmd=/tarball | |
| 284 | +[`/zip`]: /help?cmd=/zip | |
| 270 | 285 | |
| 271 | 286 | |
| 272 | 287 | ## Platform Quirks |
| 273 | 288 | |
| 274 | 289 | Fossil glob patterns are based on the glob pattern feature of POSIX |
| 275 | 290 | shells. Fossil glob patterns also have a quoting mechanism, discussed |
| 276 | -above. Because other parts of your operating system may interpret glob | |
| 291 | +[above](#syntax). Because other parts of your operating system may interpret glob | |
| 277 | 292 | patterns and quotes separately from Fossil, it is often difficult to |
| 278 | 293 | give glob patterns correctly to Fossil on the command line. Quotes and |
| 279 | 294 | special characters in glob patterns are likely to be interpreted when |
| 280 | 295 | given as part of a `fossil` command, causing unexpected behavior. |
| 281 | 296 | |
| @@ -357,16 +372,16 @@ | ||
| 357 | 372 | |
| 358 | 373 | It bears repeating that the two glob patterns here are not interpreted |
| 359 | 374 | the same way when running this command from a *subdirectory* of the top |
| 360 | 375 | checkout directory as when running it at the top of the checkout tree. |
| 361 | 376 | If these files were in a subdirectory of the checkout tree called `doc` |
| 362 | -and that was your current working directory, the command would have to | |
| 363 | -be: | |
| 377 | +and that was your current working directory, the command would instead | |
| 378 | +have to be: | |
| 364 | 379 | |
| 365 | 380 | $ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ* |
| 366 | 381 | |
| 367 | -instead. The Fossil glob pattern still needs the `doc/` prefix because | |
| 382 | +The Fossil glob pattern still needs the `doc/` prefix because | |
| 368 | 383 | Fossil always interprets glob patterns from the base of the checkout |
| 369 | 384 | directory, not from the current working directory as POSIX shells do. |
| 370 | 385 | |
| 371 | 386 | When in doubt, use `fossil status` after running commands like the |
| 372 | 387 | above to make sure the right set of files were scheduled for insertion |
| @@ -532,11 +547,13 @@ | ||
| 532 | 547 | `.fossil-settings/ignore-glob` may be possible in many cases. Here are |
| 533 | 548 | some of features of `.gitignore` and comments on how they relate to |
| 534 | 549 | Fossil: |
| 535 | 550 | |
| 536 | 551 | * "A blank line matches no files...": same in Fossil. |
| 537 | - * "A line starting with # serves as a comment....": not in Fossil. | |
| 552 | + * "A line starting with # serves as a comment...": same in Fossil, including | |
| 553 | + the possibility of escaping an initial `#` with a backslash to allow globs | |
| 554 | + beginning with a hash. | |
| 538 | 555 | * "Trailing spaces are ignored unless they are quoted..." is similar |
| 539 | 556 | in Fossil. All whitespace before and after a glob is trimmed in |
| 540 | 557 | Fossil unless quoted with single or double quotes. Git uses |
| 541 | 558 | backslash quoting instead, which Fossil does not. |
| 542 | 559 | * "An optional prefix "!" which negates the pattern...": not in |
| 543 | 560 |
| --- www/globs.md | |
| +++ www/globs.md | |
| @@ -1,50 +1,60 @@ | |
| 1 | # File Name Glob Patterns |
| 2 | |
| 3 | |
| 4 | A [glob pattern][glob] is a text expression that matches one or more |
| 5 | file names using wild cards familiar to most users of a command line. |
| 6 | For example, `*` is a glob that matches any name at all and |
| 7 | `Readme.txt` is a glob that matches exactly one file. For purposes of |
| 8 | Fossil's globs, a file name with a directory prefix is "just a string" |
| 9 | and the globs do not apply any special meaning to the directory part |
| 10 | of the name. Thus the glob `*` matches any name, including any |
| 11 | directory prefix, and `*/*` matches a name with _one or more_ |
| 12 | directory components. |
| 13 | |
| 14 | A glob should not be confused with a [regular expression][regexp] (RE), |
| 15 | even though they use some of the same special characters for similar |
| 16 | purposes, because [they are not fully compatible][greinc] pattern |
| 17 | matching languages. Fossil uses globs when matching file names with the |
| 18 | settings described in this document, not REs. |
| 19 | |
| 20 | [glob]: https://en.wikipedia.org/wiki/Glob_(programming) |
| 21 | [greinc]: https://unix.stackexchange.com/a/57958/138 |
| 22 | [regexp]: https://en.wikipedia.org/wiki/Regular_expression |
| 23 | |
| 24 | These settings hold one or more file glob patterns to cause Fossil to |
| 25 | give matching named files special treatment. Glob patterns are also |
| 26 | accepted in options to certain commands and as query parameters to |
| 27 | certain Fossil UI web pages. |
| 28 | |
| 29 | Where Fossil also accepts globs in commands, this handling may interact |
| 30 | with your OS’s command shell or its C runtime system, because they may |
| 31 | have their own glob pattern handling. We will detail such interactions |
| 32 | below. |
| 33 | |
| 34 | |
| 35 | ## Syntax |
| 36 | |
| 37 | Where Fossil accepts glob patterns, it will usually accept a *list* of |
| 38 | such patterns, each individual pattern separated from the others |
| 39 | by white space or commas. If a glob must contain white spaces or |
| 40 | commas, it can be quoted with either single or double quotation marks. |
| 41 | A list is said to match if any one glob in the list |
| 42 | matches. |
| 43 | |
| 44 | A glob pattern matches a given file name if it successfully consumes and |
| 45 | matches the *entire* name. Partial matches are failed matches. |
| 46 | |
| 47 | Most characters in a glob pattern consume a single character of the file |
| 48 | name and must match it exactly. For instance, “a” in a glob simply |
| 49 | matches the letter “a” in the file name unless it is inside a special |
| 50 | character sequence. |
| @@ -61,11 +71,11 @@ | |
| 61 | |
| 62 | Note that unlike [POSIX globs][pg], these special characters and |
| 63 | sequences are allowed to match `/` directory separators as well as the |
| 64 | initial `.` in the name of a hidden file or directory. This is because |
| 65 | Fossil file names are stored as complete path names. The distinction |
| 66 | between file name and directory name is “below” Fossil in this sense. |
| 67 | |
| 68 | [pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 |
| 69 | |
| 70 | The bracket expressions above require some additional explanation: |
| 71 | |
| @@ -163,14 +173,15 @@ | |
| 163 | `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. |
| 164 | `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. |
| 165 | `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. |
| 166 | `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. |
| 167 | `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. |
| 168 | |
| 169 | ## Where Globs are Used |
| 170 | |
| 171 | ### Settings that are Globs |
| 172 | |
| 173 | These settings are all lists of glob patterns: |
| 174 | |
| 175 | :Setting |:Description |
| 176 | -------------------------------------------------------------------------------- |
| @@ -197,12 +208,16 @@ | |
| 197 | commands. This is especially true when one (or more) IDEs are used in |
| 198 | a project because each IDE has its own ideas of how and where to cache |
| 199 | information that speeds up its browsing and building tasks but which |
| 200 | need not be preserved in your project's history. |
| 201 | |
| 202 | |
| 203 | ### Commands that Refer to Globs |
| 204 | |
| 205 | Many of the commands that respect the settings containing globs have |
| 206 | options to override some or all of the settings. These options are |
| 207 | usually named to correspond to the setting they override, such as |
| 208 | `--ignore` to override the `ignore-glob` setting. These commands are: |
| @@ -227,29 +242,29 @@ | |
| 227 | The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that |
| 228 | implement or support with web servers provide a mechanism to name some |
| 229 | files to serve with static content where a list of glob patterns |
| 230 | specifies what content may be served. |
| 231 | |
| 232 | [`add`]: /help?cmd=add |
| 233 | [`addremove`]: /help?cmd=addremove |
| 234 | [`changes`]: /help?cmd=changes |
| 235 | [`clean`]: /help?cmd=clean |
| 236 | [`commit`]: /help?cmd=commit |
| 237 | [`extras`]: /help?cmd=extras |
| 238 | [`merge`]: /help?cmd=merge |
| 239 | [`settings`]: /help?cmd=settings |
| 240 | [`status`]: /help?cmd=status |
| 241 | [`touch`]: /help?cmd=touch |
| 242 | [`unset`]: /help?cmd=unset |
| 243 | |
| 244 | [`tarball`]: /help?cmd=tarball |
| 245 | [`zip`]: /help?cmd=zip |
| 246 | |
| 247 | [`http`]: /help?cmd=http |
| 248 | [`cgi`]: /help?cmd=cgi |
| 249 | [`server`]: /help?cmd=server |
| 250 | [`ui`]: /help?cmd=ui |
| 251 | |
| 252 | |
| 253 | ### Web Pages that Refer to Globs |
| 254 | |
| 255 | The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that |
| @@ -263,19 +278,19 @@ | |
| 263 | of a specific checkin. They may be further restricted by query |
| 264 | parameters that specify glob patterns that name files to include or |
| 265 | exclude rather than taking the entire checkin. |
| 266 | |
| 267 | [`/timeline`]: /help?cmd=/timeline |
| 268 | [`/tarball`]: /help?cmd=/tarball |
| 269 | [`/zip`]: /help?cmd=/zip |
| 270 | |
| 271 | |
| 272 | ## Platform Quirks |
| 273 | |
| 274 | Fossil glob patterns are based on the glob pattern feature of POSIX |
| 275 | shells. Fossil glob patterns also have a quoting mechanism, discussed |
| 276 | above. Because other parts of your operating system may interpret glob |
| 277 | patterns and quotes separately from Fossil, it is often difficult to |
| 278 | give glob patterns correctly to Fossil on the command line. Quotes and |
| 279 | special characters in glob patterns are likely to be interpreted when |
| 280 | given as part of a `fossil` command, causing unexpected behavior. |
| 281 | |
| @@ -357,16 +372,16 @@ | |
| 357 | |
| 358 | It bears repeating that the two glob patterns here are not interpreted |
| 359 | the same way when running this command from a *subdirectory* of the top |
| 360 | checkout directory as when running it at the top of the checkout tree. |
| 361 | If these files were in a subdirectory of the checkout tree called `doc` |
| 362 | and that was your current working directory, the command would have to |
| 363 | be: |
| 364 | |
| 365 | $ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ* |
| 366 | |
| 367 | instead. The Fossil glob pattern still needs the `doc/` prefix because |
| 368 | Fossil always interprets glob patterns from the base of the checkout |
| 369 | directory, not from the current working directory as POSIX shells do. |
| 370 | |
| 371 | When in doubt, use `fossil status` after running commands like the |
| 372 | above to make sure the right set of files were scheduled for insertion |
| @@ -532,11 +547,13 @@ | |
| 532 | `.fossil-settings/ignore-glob` may be possible in many cases. Here are |
| 533 | some of features of `.gitignore` and comments on how they relate to |
| 534 | Fossil: |
| 535 | |
| 536 | * "A blank line matches no files...": same in Fossil. |
| 537 | * "A line starting with # serves as a comment....": not in Fossil. |
| 538 | * "Trailing spaces are ignored unless they are quoted..." is similar |
| 539 | in Fossil. All whitespace before and after a glob is trimmed in |
| 540 | Fossil unless quoted with single or double quotes. Git uses |
| 541 | backslash quoting instead, which Fossil does not. |
| 542 | * "An optional prefix "!" which negates the pattern...": not in |
| 543 |
| --- www/globs.md | |
| +++ www/globs.md | |
| @@ -1,50 +1,60 @@ | |
| 1 | # File Name Glob Patterns |
| 2 | |
| 3 | A [glob pattern][glob] is a text expression that matches one or more |
| 4 | file names using wildcards familiar to most users of a command line. |
| 5 | For example, `*` is a glob that matches any name at all, and |
| 6 | `Readme.txt` is a glob that matches exactly one file. For purposes of |
| 7 | Fossil's globs, a complete path name is just a string, |
| 8 | and the globs do not apply any special meaning to the directory part |
| 9 | of the name. Thus, the glob `*` matches any name, including any |
| 10 | directory prefix, and `*/*` matches a name with _one or more_ |
| 11 | directory components. |
| 12 | |
| 13 | A glob should not be confused with a [regular expression][regexp] (RE) |
| 14 | even though they use some of the same special characters for similar |
| 15 | purposes. [They are not fully compatible][greinc] pattern |
| 16 | matching languages. Fossil uses globs when matching file names with the |
| 17 | settings described in this document, not REs. |
| 18 | |
| 19 | [glob]: https://en.wikipedia.org/wiki/Glob_(programming) |
| 20 | [greinc]: https://unix.stackexchange.com/a/57958/138 |
| 21 | [regexp]: https://en.wikipedia.org/wiki/Regular_expression |
| 22 | |
| 23 | [Fossil’s `*-glob` settings](#settings) hold one or more patterns to cause Fossil to |
| 24 | give matching named files special treatment. Glob patterns are also |
| 25 | accepted in options to certain commands and as query parameters to |
| 26 | certain Fossil UI web pages. For consistency, settings such as |
| 27 | `empty-dirs` are parsed as a glob even though they aren’t then *applied* |
| 28 | as a glob since it allows [the same syntax rules](#syntax) to apply. |
| 29 | |
| 30 | Where Fossil also accepts globs in commands, this handling may interact |
| 31 | with your OS’s command shell or its C runtime system, because they may |
| 32 | have their own glob pattern handling. We will detail such interactions |
| 33 | below. |
| 34 | |
| 35 | |
| 36 | ## <a id="syntax"></a>Syntax |
| 37 | |
| 38 | Where Fossil accepts glob patterns, it will usually accept a *list* of |
| 39 | individual patterns separated from the others by whitespace or commas. |
| 40 | |
| 41 | The parser allows whitespace and commas in a pattern by quoting _the |
| 42 | entire pattern_ with either single or double quotation marks. Internal |
| 43 | quotation marks are treated literally. Moreover, a pattern that begins |
| 44 | with a quote mark ends when the first instance of the same mark occurs, |
| 45 | _not_ at a whitespace or comma. Thus, this: |
| 46 | |
| 47 | "foo bar"qux |
| 48 | |
| 49 | …constitutes _two_ patterns rather than one with an embedded space, in |
| 50 | contravention of normal shell quoting rules. |
| 51 | |
| 52 | A list matches a file when any pattern in that list matches. |
| 53 | |
| 54 | A pattern must consume and |
| 55 | match the *entire* file name to succeed. Partial matches are failed matches. |
| 56 | |
| 57 | Most characters in a glob pattern consume a single character of the file |
| 58 | name and must match it exactly. For instance, “a” in a glob simply |
| 59 | matches the letter “a” in the file name unless it is inside a special |
| 60 | character sequence. |
| @@ -61,11 +71,11 @@ | |
| 71 | |
| 72 | Note that unlike [POSIX globs][pg], these special characters and |
| 73 | sequences are allowed to match `/` directory separators as well as the |
| 74 | initial `.` in the name of a hidden file or directory. This is because |
| 75 | Fossil file names are stored as complete path names. The distinction |
| 76 | between file name and directory name is “underneath” Fossil in this sense. |
| 77 | |
| 78 | [pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 |
| 79 | |
| 80 | The bracket expressions above require some additional explanation: |
| 81 | |
| @@ -163,14 +173,15 @@ | |
| 173 | `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. |
| 174 | `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. |
| 175 | `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. |
| 176 | `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. |
| 177 | `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. |
| 178 | |
| 179 | |
| 180 | ## Where Globs are Used |
| 181 | |
| 182 | ### <a id="settings"></a>Settings that are Globs |
| 183 | |
| 184 | These settings are all lists of glob patterns: |
| 185 | |
| 186 | :Setting |:Description |
| 187 | -------------------------------------------------------------------------------- |
| @@ -197,12 +208,16 @@ | |
| 208 | commands. This is especially true when one (or more) IDEs are used in |
| 209 | a project because each IDE has its own ideas of how and where to cache |
| 210 | information that speeds up its browsing and building tasks but which |
| 211 | need not be preserved in your project's history. |
| 212 | |
| 213 | Although the `empty-dirs` setting is not a list of glob patterns as |
| 214 | such, it is *parsed* that way for consistency among the settings, |
| 215 | allowing [the list parsing rules above](#syntax) to apply. |
| 216 | |
| 217 | |
| 218 | ### <a id="commands"></a>Commands that Refer to Globs |
| 219 | |
| 220 | Many of the commands that respect the settings containing globs have |
| 221 | options to override some or all of the settings. These options are |
| 222 | usually named to correspond to the setting they override, such as |
| 223 | `--ignore` to override the `ignore-glob` setting. These commands are: |
| @@ -227,29 +242,29 @@ | |
| 242 | The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that |
| 243 | implement or support with web servers provide a mechanism to name some |
| 244 | files to serve with static content where a list of glob patterns |
| 245 | specifies what content may be served. |
| 246 | |
| 247 | [`add`]: /help?cmd=add |
| 248 | [`addremove`]: /help?cmd=addremove |
| 249 | [`changes`]: /help?cmd=changes |
| 250 | [`clean`]: /help?cmd=clean |
| 251 | [`commit`]: /help?cmd=commit |
| 252 | [`extras`]: /help?cmd=extras |
| 253 | [`merge`]: /help?cmd=merge |
| 254 | [`settings`]: /help?cmd=settings |
| 255 | [`status`]: /help?cmd=status |
| 256 | [`touch`]: /help?cmd=touch |
| 257 | [`unset`]: /help?cmd=unset |
| 258 | |
| 259 | [`tarball`]: /help?cmd=tarball |
| 260 | [`zip`]: /help?cmd=zip |
| 261 | |
| 262 | [`http`]: /help?cmd=http |
| 263 | [`cgi`]: /help?cmd=cgi |
| 264 | [`server`]: /help?cmd=server |
| 265 | [`ui`]: /help?cmd=ui |
| 266 | |
| 267 | |
| 268 | ### Web Pages that Refer to Globs |
| 269 | |
| 270 | The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that |
| @@ -263,19 +278,19 @@ | |
| 278 | of a specific checkin. They may be further restricted by query |
| 279 | parameters that specify glob patterns that name files to include or |
| 280 | exclude rather than taking the entire checkin. |
| 281 | |
| 282 | [`/timeline`]: /help?cmd=/timeline |
| 283 | [`/tarball`]: /help?cmd=/tarball |
| 284 | [`/zip`]: /help?cmd=/zip |
| 285 | |
| 286 | |
| 287 | ## Platform Quirks |
| 288 | |
| 289 | Fossil glob patterns are based on the glob pattern feature of POSIX |
| 290 | shells. Fossil glob patterns also have a quoting mechanism, discussed |
| 291 | [above](#syntax). Because other parts of your operating system may interpret glob |
| 292 | patterns and quotes separately from Fossil, it is often difficult to |
| 293 | give glob patterns correctly to Fossil on the command line. Quotes and |
| 294 | special characters in glob patterns are likely to be interpreted when |
| 295 | given as part of a `fossil` command, causing unexpected behavior. |
| 296 | |
| @@ -357,16 +372,16 @@ | |
| 372 | |
| 373 | It bears repeating that the two glob patterns here are not interpreted |
| 374 | the same way when running this command from a *subdirectory* of the top |
| 375 | checkout directory as when running it at the top of the checkout tree. |
| 376 | If these files were in a subdirectory of the checkout tree called `doc` |
| 377 | and that was your current working directory, the command would instead |
| 378 | have to be: |
| 379 | |
| 380 | $ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ* |
| 381 | |
| 382 | The Fossil glob pattern still needs the `doc/` prefix because |
| 383 | Fossil always interprets glob patterns from the base of the checkout |
| 384 | directory, not from the current working directory as POSIX shells do. |
| 385 | |
| 386 | When in doubt, use `fossil status` after running commands like the |
| 387 | above to make sure the right set of files were scheduled for insertion |
| @@ -532,11 +547,13 @@ | |
| 547 | `.fossil-settings/ignore-glob` may be possible in many cases. Here are |
| 548 | some of features of `.gitignore` and comments on how they relate to |
| 549 | Fossil: |
| 550 | |
| 551 | * "A blank line matches no files...": same in Fossil. |
| 552 | * "A line starting with # serves as a comment...": same in Fossil, including |
| 553 | the possibility of escaping an initial `#` with a backslash to allow globs |
| 554 | beginning with a hash. |
| 555 | * "Trailing spaces are ignored unless they are quoted..." is similar |
| 556 | in Fossil. All whitespace before and after a glob is trimmed in |
| 557 | Fossil unless quoted with single or double quotes. Git uses |
| 558 | backslash quoting instead, which Fossil does not. |
| 559 | * "An optional prefix "!" which negates the pattern...": not in |
| 560 |
+19
-20
| --- www/image-format-vs-repo-size.md | ||
| +++ www/image-format-vs-repo-size.md | ||
| @@ -1,19 +1,19 @@ | ||
| 1 | 1 | # Image Format vs Fossil Repo Size |
| 2 | 2 | |
| 3 | 3 | ## The Problem |
| 4 | 4 | |
| 5 | 5 | Fossil has a [delta compression][dc] feature which removes redundant |
| 6 | -information from a file relative to its parent on check-in.¹ | |
| 6 | +information from a file relative to its parent on check-in.[^delta-prgs] | |
| 7 | 7 | That delta is then [zlib][zl]-compressed before being stored |
| 8 | 8 | in the Fossil repository database file. |
| 9 | 9 | |
| 10 | 10 | Storing pre-compressed data files in a Fossil repository defeats both of |
| 11 | 11 | these space-saving measures: |
| 12 | 12 | |
| 13 | 13 | 1. Binary data compression algorithms turn the file data into |
| 14 | - [pseudorandom noise][prn].² | |
| 14 | + pseudorandom noise.[^prn] | |
| 15 | 15 | |
| 16 | 16 | Typical data compression algorithms are not [hash functions][hf], |
| 17 | 17 | where the goal is that a change to each bit in the input has a |
| 18 | 18 | statistically even chance of changing every bit in the output, but |
| 19 | 19 | because they do approach that pathological condition, pre-compressed |
| @@ -34,11 +34,10 @@ | ||
| 34 | 34 | the input data change on each checkin. This article will illustrate that |
| 35 | 35 | problem, quantify it, and give a solution to it. |
| 36 | 36 | |
| 37 | 37 | [dc]: ./delta_format.wiki |
| 38 | 38 | [hf]: https://en.wikipedia.org/wiki/Hash_function |
| 39 | -[prn]: https://en.wikipedia.org/wiki/Pseudorandomness | |
| 40 | 39 | [zl]: http://www.zlib.net/ |
| 41 | 40 | |
| 42 | 41 | |
| 43 | 42 | ## <a id="formats"></a>Affected File Formats |
| 44 | 43 | |
| @@ -93,11 +92,11 @@ | ||
| 93 | 92 | image, check it in, and remember the new Fossil repo size. |
| 94 | 93 | |
| 95 | 94 | 5. Iterate on step 4 some number of times — currently 10 — and remember |
| 96 | 95 | the Fossil repo size at each step. |
| 97 | 96 | |
| 98 | -6. Repeat the above steps for BMP, TIFF,³ and PNG. | |
| 97 | +6. Repeat the above steps for BMP, PNG, and TIFF.[^tiff-cmp] | |
| 99 | 98 | |
| 100 | 99 | 7. Create a bar chart showing how the Fossil repository size changes |
| 101 | 100 | with each checkin. |
| 102 | 101 | |
| 103 | 102 | We chose to use JupyterLab for this because it makes it easy for you to |
| @@ -115,20 +114,20 @@ | ||
| 115 | 114 | [wp]: http://wand-py.org/ |
| 116 | 115 | |
| 117 | 116 | |
| 118 | 117 | ## <a id="results"></a>Results |
| 119 | 118 | |
| 120 | -Running the notebook gives a bar chart something like⁴ this: | |
| 119 | +Running the notebook gives a bar chart something like[^variance] this: | |
| 121 | 120 | |
| 122 | 121 |  |
| 123 | 122 | |
| 124 | 123 | There are a few key things we want to draw your attention to in that |
| 125 | 124 | chart: |
| 126 | 125 | |
| 127 | 126 | * BMP and uncompressed TIFF are nearly identical in size for all |
| 128 | 127 | checkins, and the repository growth rate is negligible past the |
| 129 | - first commit.⁵ We owe this economy to Fossil’s delta compression | |
| 128 | + first commit.[^size-jump] We owe this economy to Fossil’s delta compression | |
| 130 | 129 | feature: it is encoding each of those single-pixel changes in a very |
| 131 | 130 | small amount of repository space. |
| 132 | 131 | |
| 133 | 132 | * The JPEG and PNG bars increase by large amounts on most checkins |
| 134 | 133 | even though each checkin *also* encodes only a *single-pixel change*. |
| @@ -158,11 +157,11 @@ | ||
| 158 | 157 | Since programs that produce and consume binary-compressed data files |
| 159 | 158 | often make it either difficult or impossible to work with the |
| 160 | 159 | uncompressed form, we want an automated method for producing the |
| 161 | 160 | uncompressed form to make Fossil happy while still having the compressed |
| 162 | 161 | form to keep our content creation applications happy. This `Makefile` |
| 163 | -should⁶ do that for BMP, PNG, SVG, and XLSX files: | |
| 162 | +should[^makefile] do that for BMP, PNG, SVG, and XLSX files: | |
| 164 | 163 | |
| 165 | 164 | .SUFFIXES: .bmp .png .svg .svgz |
| 166 | 165 | |
| 167 | 166 | .svgz.svg: |
| 168 | 167 | gzip -dc < $< > $@ |
| @@ -194,11 +193,11 @@ | ||
| 194 | 193 | |
| 195 | 194 | This `Makefile` allows you to treat the compressed version as the |
| 196 | 195 | process input, but to actually check in only the changes against the |
| 197 | 196 | uncompressed version by typing “`make`” before “`fossil ci`”. This is |
| 198 | 197 | not actually an extra step in practice, since if you’ve got a |
| 199 | -`Makefile`-based project, you should be building (and testing!) it | |
| 198 | +`Makefile`-based project, you should be building — and testing — it | |
| 200 | 199 | before checking each change in anyway! |
| 201 | 200 | |
| 202 | 201 | Because this technique is based on dependency rules, only the necessary |
| 203 | 202 | files are generated on each `make` command. |
| 204 | 203 | |
| @@ -239,66 +238,65 @@ | ||
| 239 | 238 | automatically-uncompressed PDF for the benefit of Fossil. Unlike |
| 240 | 239 | with the Excel case, there is no simple “file base name to directory |
| 241 | 240 | name” mapping, so we just created the `-big` to `-small` name scheme |
| 242 | 241 | here. |
| 243 | 242 | |
| ----- | ||
| 244 | 243 | |
| 245 | - | |
| 246 | -## <a id="notes"></a>Footnotes and Digressions | |
| 247 | - | |
| 248 | -1. This problem is not Fossil-specific. Several other programs also do | |
| 244 | +[^delta-prgs]: | |
| 245 | + This problem is not Fossil-specific. Several other programs also do | |
| 249 | 246 | delta compression, so they’ll also be affected by this problem: |
| 250 | 247 | [rsync][rs], [Unison][us], [Git][git], etc. You should take this |
| 251 | 248 | article’s advice when using all such programs, not just Fossil. |
| 252 | - | |
| 253 | 249 | When using file copying and synchronization programs *without* delta |
| 254 | 250 | compression, on the other hand, it’s best to use the most |
| 255 | 251 | highly-compressed file format you can tolerate, since they copy the |
| 256 | 252 | whole file any time any bit of it changes. |
| 257 | 253 | |
| 258 | -2. In fact, a good way to gauge the effectiveness of a given | |
| 254 | +[^prn]: | |
| 255 | + In fact, a good way to gauge the effectiveness of a given | |
| 259 | 256 | compression scheme is to run its output through the same sort of |
| 260 | 257 | tests we use to gauge how “random” a given [PRNG][prng] is. Another |
| 261 | 258 | way to look at it is that if there is a discernible pattern in the |
| 262 | 259 | output of a compression scheme, that constitutes *information* (in |
| 263 | 260 | [the technical sense of that word][ith]) that could be further |
| 264 | 261 | compressed. |
| 265 | 262 | |
| 266 | -3. We're using *uncompressed* TIFF here, not [LZW][lzw]- or | |
| 263 | +[^tiff-cmp]: | |
| 264 | + We're using *uncompressed* TIFF here, not [LZW][lzw]- or | |
| 267 | 265 | Zip-compressed TIFF, either of which would give similar results to |
| 268 | 266 | PNG, which is always zlib-compressed. |
| 269 | 267 | |
| 270 | -4. The raw data changes somewhat from one run to the next due to the | |
| 268 | +[^variance]: | |
| 269 | + The raw data changes somewhat from one run to the next due to the | |
| 271 | 270 | use of random noise in the image to make the zlib/PNG compression |
| 272 | 271 | more difficult, and the random pixel changes. Those test design |
| 273 | 272 | choices make this a [Monte Carlo experiment][mce]. We’ve found that |
| 274 | 273 | the overall character of the results doesn’t change from one run to |
| 275 | 274 | the next. |
| 276 | 275 | |
| 277 | -5. It’s not clear to me why there is a one-time jump in size for BMP | |
| 276 | +[^size-jump]: | |
| 277 | + It’s not clear to me why there is a one-time jump in size for BMP | |
| 278 | 278 | and TIFF past the first commit. I suspect it is due to the SQLite |
| 279 | 279 | indices being initialized for the first time. |
| 280 | - | |
| 281 | 280 | Page size inflation might have something to do with it as well, |
| 282 | 281 | though we tried to control that by rebuilding the initial DB with a |
| 283 | 282 | minimal page size. If you re-run the program often enough, you will |
| 284 | 283 | sometimes see the BMP or TIFF bar jump higher than the other, again |
| 285 | 284 | likely due to one of the repos crossing a page boundary. |
| 286 | - | |
| 287 | 285 | Another curious artifact in the data is that the BMP is slightly |
| 288 | 286 | larger than for the TIFF. This goes against expectation because a |
| 289 | 287 | low-tech format like BMP should have a small edge in this test |
| 290 | 288 | because TIFF metadata includes the option for multiple timestamps, |
| 291 | 289 | UUIDs, etc., which bloat the checkin size by creating many small |
| 292 | 290 | deltas. |
| 293 | 291 | |
| 294 | -6. The `Makefile` above is not battle-tested. Please report bugs and | |
| 292 | +[^makefile]: | |
| 293 | + The `Makefile` above is not battle-tested. Please report bugs and | |
| 295 | 294 | needed extensions [on the forum][for]. |
| 296 | 295 | |
| 297 | 296 | [for]: https://fossil-scm.org/forum/forumpost/15e677f2c8 |
| 298 | 297 | [git]: https://git-scm.com/ |
| 299 | 298 | [ith]: https://en.wikipedia.org/wiki/Information_theory |
| 300 | 299 | [lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch |
| 301 | 300 | [prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator |
| 302 | 301 | [rs]: https://rsync.samba.org/ |
| 303 | 302 | [us]: http://www.cis.upenn.edu/~bcpierce/unison/ |
| 304 | 303 |
| --- www/image-format-vs-repo-size.md | |
| +++ www/image-format-vs-repo-size.md | |
| @@ -1,19 +1,19 @@ | |
| 1 | # Image Format vs Fossil Repo Size |
| 2 | |
| 3 | ## The Problem |
| 4 | |
| 5 | Fossil has a [delta compression][dc] feature which removes redundant |
| 6 | information from a file relative to its parent on check-in.¹ |
| 7 | That delta is then [zlib][zl]-compressed before being stored |
| 8 | in the Fossil repository database file. |
| 9 | |
| 10 | Storing pre-compressed data files in a Fossil repository defeats both of |
| 11 | these space-saving measures: |
| 12 | |
| 13 | 1. Binary data compression algorithms turn the file data into |
| 14 | [pseudorandom noise][prn].² |
| 15 | |
| 16 | Typical data compression algorithms are not [hash functions][hf], |
| 17 | where the goal is that a change to each bit in the input has a |
| 18 | statistically even chance of changing every bit in the output, but |
| 19 | because they do approach that pathological condition, pre-compressed |
| @@ -34,11 +34,10 @@ | |
| 34 | the input data change on each checkin. This article will illustrate that |
| 35 | problem, quantify it, and give a solution to it. |
| 36 | |
| 37 | [dc]: ./delta_format.wiki |
| 38 | [hf]: https://en.wikipedia.org/wiki/Hash_function |
| 39 | [prn]: https://en.wikipedia.org/wiki/Pseudorandomness |
| 40 | [zl]: http://www.zlib.net/ |
| 41 | |
| 42 | |
| 43 | ## <a id="formats"></a>Affected File Formats |
| 44 | |
| @@ -93,11 +92,11 @@ | |
| 93 | image, check it in, and remember the new Fossil repo size. |
| 94 | |
| 95 | 5. Iterate on step 4 some number of times — currently 10 — and remember |
| 96 | the Fossil repo size at each step. |
| 97 | |
| 98 | 6. Repeat the above steps for BMP, TIFF,³ and PNG. |
| 99 | |
| 100 | 7. Create a bar chart showing how the Fossil repository size changes |
| 101 | with each checkin. |
| 102 | |
| 103 | We chose to use JupyterLab for this because it makes it easy for you to |
| @@ -115,20 +114,20 @@ | |
| 115 | [wp]: http://wand-py.org/ |
| 116 | |
| 117 | |
| 118 | ## <a id="results"></a>Results |
| 119 | |
| 120 | Running the notebook gives a bar chart something like⁴ this: |
| 121 | |
| 122 |  |
| 123 | |
| 124 | There are a few key things we want to draw your attention to in that |
| 125 | chart: |
| 126 | |
| 127 | * BMP and uncompressed TIFF are nearly identical in size for all |
| 128 | checkins, and the repository growth rate is negligible past the |
| 129 | first commit.⁵ We owe this economy to Fossil’s delta compression |
| 130 | feature: it is encoding each of those single-pixel changes in a very |
| 131 | small amount of repository space. |
| 132 | |
| 133 | * The JPEG and PNG bars increase by large amounts on most checkins |
| 134 | even though each checkin *also* encodes only a *single-pixel change*. |
| @@ -158,11 +157,11 @@ | |
| 158 | Since programs that produce and consume binary-compressed data files |
| 159 | often make it either difficult or impossible to work with the |
| 160 | uncompressed form, we want an automated method for producing the |
| 161 | uncompressed form to make Fossil happy while still having the compressed |
| 162 | form to keep our content creation applications happy. This `Makefile` |
| 163 | should⁶ do that for BMP, PNG, SVG, and XLSX files: |
| 164 | |
| 165 | .SUFFIXES: .bmp .png .svg .svgz |
| 166 | |
| 167 | .svgz.svg: |
| 168 | gzip -dc < $< > $@ |
| @@ -194,11 +193,11 @@ | |
| 194 | |
| 195 | This `Makefile` allows you to treat the compressed version as the |
| 196 | process input, but to actually check in only the changes against the |
| 197 | uncompressed version by typing “`make`” before “`fossil ci`”. This is |
| 198 | not actually an extra step in practice, since if you’ve got a |
| 199 | `Makefile`-based project, you should be building (and testing!) it |
| 200 | before checking each change in anyway! |
| 201 | |
| 202 | Because this technique is based on dependency rules, only the necessary |
| 203 | files are generated on each `make` command. |
| 204 | |
| @@ -239,66 +238,65 @@ | |
| 239 | automatically-uncompressed PDF for the benefit of Fossil. Unlike |
| 240 | with the Excel case, there is no simple “file base name to directory |
| 241 | name” mapping, so we just created the `-big` to `-small` name scheme |
| 242 | here. |
| 243 | |
| ----- | |
| 244 | |
| 245 | |
| 246 | ## <a id="notes"></a>Footnotes and Digressions |
| 247 | |
| 248 | 1. This problem is not Fossil-specific. Several other programs also do |
| 249 | delta compression, so they’ll also be affected by this problem: |
| 250 | [rsync][rs], [Unison][us], [Git][git], etc. You should take this |
| 251 | article’s advice when using all such programs, not just Fossil. |
| 252 | |
| 253 | When using file copying and synchronization programs *without* delta |
| 254 | compression, on the other hand, it’s best to use the most |
| 255 | highly-compressed file format you can tolerate, since they copy the |
| 256 | whole file any time any bit of it changes. |
| 257 | |
| 258 | 2. In fact, a good way to gauge the effectiveness of a given |
| 259 | compression scheme is to run its output through the same sort of |
| 260 | tests we use to gauge how “random” a given [PRNG][prng] is. Another |
| 261 | way to look at it is that if there is a discernible pattern in the |
| 262 | output of a compression scheme, that constitutes *information* (in |
| 263 | [the technical sense of that word][ith]) that could be further |
| 264 | compressed. |
| 265 | |
| 266 | 3. We're using *uncompressed* TIFF here, not [LZW][lzw]- or |
| 267 | Zip-compressed TIFF, either of which would give similar results to |
| 268 | PNG, which is always zlib-compressed. |
| 269 | |
| 270 | 4. The raw data changes somewhat from one run to the next due to the |
| 271 | use of random noise in the image to make the zlib/PNG compression |
| 272 | more difficult, and the random pixel changes. Those test design |
| 273 | choices make this a [Monte Carlo experiment][mce]. We’ve found that |
| 274 | the overall character of the results doesn’t change from one run to |
| 275 | the next. |
| 276 | |
| 277 | 5. It’s not clear to me why there is a one-time jump in size for BMP |
| 278 | and TIFF past the first commit. I suspect it is due to the SQLite |
| 279 | indices being initialized for the first time. |
| 280 | |
| 281 | Page size inflation might have something to do with it as well, |
| 282 | though we tried to control that by rebuilding the initial DB with a |
| 283 | minimal page size. If you re-run the program often enough, you will |
| 284 | sometimes see the BMP or TIFF bar jump higher than the other, again |
| 285 | likely due to one of the repos crossing a page boundary. |
| 286 | |
| 287 | Another curious artifact in the data is that the BMP is slightly |
| 288 | larger than for the TIFF. This goes against expectation because a |
| 289 | low-tech format like BMP should have a small edge in this test |
| 290 | because TIFF metadata includes the option for multiple timestamps, |
| 291 | UUIDs, etc., which bloat the checkin size by creating many small |
| 292 | deltas. |
| 293 | |
| 294 | 6. The `Makefile` above is not battle-tested. Please report bugs and |
| 295 | needed extensions [on the forum][for]. |
| 296 | |
| 297 | [for]: https://fossil-scm.org/forum/forumpost/15e677f2c8 |
| 298 | [git]: https://git-scm.com/ |
| 299 | [ith]: https://en.wikipedia.org/wiki/Information_theory |
| 300 | [lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch |
| 301 | [prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator |
| 302 | [rs]: https://rsync.samba.org/ |
| 303 | [us]: http://www.cis.upenn.edu/~bcpierce/unison/ |
| 304 |
| --- www/image-format-vs-repo-size.md | |
| +++ www/image-format-vs-repo-size.md | |
| @@ -1,19 +1,19 @@ | |
| 1 | # Image Format vs Fossil Repo Size |
| 2 | |
| 3 | ## The Problem |
| 4 | |
| 5 | Fossil has a [delta compression][dc] feature which removes redundant |
| 6 | information from a file relative to its parent on check-in.[^delta-prgs] |
| 7 | That delta is then [zlib][zl]-compressed before being stored |
| 8 | in the Fossil repository database file. |
| 9 | |
| 10 | Storing pre-compressed data files in a Fossil repository defeats both of |
| 11 | these space-saving measures: |
| 12 | |
| 13 | 1. Binary data compression algorithms turn the file data into |
| 14 | pseudorandom noise.[^prn] |
| 15 | |
| 16 | Typical data compression algorithms are not [hash functions][hf], |
| 17 | where the goal is that a change to each bit in the input has a |
| 18 | statistically even chance of changing every bit in the output, but |
| 19 | because they do approach that pathological condition, pre-compressed |
| @@ -34,11 +34,10 @@ | |
| 34 | the input data change on each checkin. This article will illustrate that |
| 35 | problem, quantify it, and give a solution to it. |
| 36 | |
| 37 | [dc]: ./delta_format.wiki |
| 38 | [hf]: https://en.wikipedia.org/wiki/Hash_function |
| 39 | [zl]: http://www.zlib.net/ |
| 40 | |
| 41 | |
| 42 | ## <a id="formats"></a>Affected File Formats |
| 43 | |
| @@ -93,11 +92,11 @@ | |
| 92 | image, check it in, and remember the new Fossil repo size. |
| 93 | |
| 94 | 5. Iterate on step 4 some number of times — currently 10 — and remember |
| 95 | the Fossil repo size at each step. |
| 96 | |
| 97 | 6. Repeat the above steps for BMP, PNG, and TIFF.[^tiff-cmp] |
| 98 | |
| 99 | 7. Create a bar chart showing how the Fossil repository size changes |
| 100 | with each checkin. |
| 101 | |
| 102 | We chose to use JupyterLab for this because it makes it easy for you to |
| @@ -115,20 +114,20 @@ | |
| 114 | [wp]: http://wand-py.org/ |
| 115 | |
| 116 | |
| 117 | ## <a id="results"></a>Results |
| 118 | |
| 119 | Running the notebook gives a bar chart something like[^variance] this: |
| 120 | |
| 121 |  |
| 122 | |
| 123 | There are a few key things we want to draw your attention to in that |
| 124 | chart: |
| 125 | |
| 126 | * BMP and uncompressed TIFF are nearly identical in size for all |
| 127 | checkins, and the repository growth rate is negligible past the |
| 128 | first commit.[^size-jump] We owe this economy to Fossil’s delta compression |
| 129 | feature: it is encoding each of those single-pixel changes in a very |
| 130 | small amount of repository space. |
| 131 | |
| 132 | * The JPEG and PNG bars increase by large amounts on most checkins |
| 133 | even though each checkin *also* encodes only a *single-pixel change*. |
| @@ -158,11 +157,11 @@ | |
| 157 | Since programs that produce and consume binary-compressed data files |
| 158 | often make it either difficult or impossible to work with the |
| 159 | uncompressed form, we want an automated method for producing the |
| 160 | uncompressed form to make Fossil happy while still having the compressed |
| 161 | form to keep our content creation applications happy. This `Makefile` |
| 162 | should[^makefile] do that for BMP, PNG, SVG, and XLSX files: |
| 163 | |
| 164 | .SUFFIXES: .bmp .png .svg .svgz |
| 165 | |
| 166 | .svgz.svg: |
| 167 | gzip -dc < $< > $@ |
| @@ -194,11 +193,11 @@ | |
| 193 | |
| 194 | This `Makefile` allows you to treat the compressed version as the |
| 195 | process input, but to actually check in only the changes against the |
| 196 | uncompressed version by typing “`make`” before “`fossil ci`”. This is |
| 197 | not actually an extra step in practice, since if you’ve got a |
| 198 | `Makefile`-based project, you should be building — and testing — it |
| 199 | before checking each change in anyway! |
| 200 | |
| 201 | Because this technique is based on dependency rules, only the necessary |
| 202 | files are generated on each `make` command. |
| 203 | |
| @@ -239,66 +238,65 @@ | |
| 238 | automatically-uncompressed PDF for the benefit of Fossil. Unlike |
| 239 | with the Excel case, there is no simple “file base name to directory |
| 240 | name” mapping, so we just created the `-big` to `-small` name scheme |
| 241 | here. |
| 242 | |
| ----- | |
| 243 | |
| 244 | [^delta-prgs]: |
| 245 | This problem is not Fossil-specific. Several other programs also do |
| 246 | delta compression, so they’ll also be affected by this problem: |
| 247 | [rsync][rs], [Unison][us], [Git][git], etc. You should take this |
| 248 | article’s advice when using all such programs, not just Fossil. |
| 249 | When using file copying and synchronization programs *without* delta |
| 250 | compression, on the other hand, it’s best to use the most |
| 251 | highly-compressed file format you can tolerate, since they copy the |
| 252 | whole file any time any bit of it changes. |
| 253 | |
| 254 | [^prn]: |
| 255 | In fact, a good way to gauge the effectiveness of a given |
| 256 | compression scheme is to run its output through the same sort of |
| 257 | tests we use to gauge how “random” a given [PRNG][prng] is. Another |
| 258 | way to look at it is that if there is a discernible pattern in the |
| 259 | output of a compression scheme, that constitutes *information* (in |
| 260 | [the technical sense of that word][ith]) that could be further |
| 261 | compressed. |
| 262 | |
| 263 | [^tiff-cmp]: |
| 264 | We're using *uncompressed* TIFF here, not [LZW][lzw]- or |
| 265 | Zip-compressed TIFF, either of which would give similar results to |
| 266 | PNG, which is always zlib-compressed. |
| 267 | |
| 268 | [^variance]: |
| 269 | The raw data changes somewhat from one run to the next due to the |
| 270 | use of random noise in the image to make the zlib/PNG compression |
| 271 | more difficult, and the random pixel changes. Those test design |
| 272 | choices make this a [Monte Carlo experiment][mce]. We’ve found that |
| 273 | the overall character of the results doesn’t change from one run to |
| 274 | the next. |
| 275 | |
| 276 | [^size-jump]: |
| 277 | It’s not clear to me why there is a one-time jump in size for BMP |
| 278 | and TIFF past the first commit. I suspect it is due to the SQLite |
| 279 | indices being initialized for the first time. |
| 280 | Page size inflation might have something to do with it as well, |
| 281 | though we tried to control that by rebuilding the initial DB with a |
| 282 | minimal page size. If you re-run the program often enough, you will |
| 283 | sometimes see the BMP or TIFF bar jump higher than the other, again |
| 284 | likely due to one of the repos crossing a page boundary. |
| 285 | Another curious artifact in the data is that the BMP is slightly |
| 286 | larger than for the TIFF. This goes against expectation because a |
| 287 | low-tech format like BMP should have a small edge in this test |
| 288 | because TIFF metadata includes the option for multiple timestamps, |
| 289 | UUIDs, etc., which bloat the checkin size by creating many small |
| 290 | deltas. |
| 291 | |
| 292 | [^makefile]: |
| 293 | The `Makefile` above is not battle-tested. Please report bugs and |
| 294 | needed extensions [on the forum][for]. |
| 295 | |
| 296 | [for]: https://fossil-scm.org/forum/forumpost/15e677f2c8 |
| 297 | [git]: https://git-scm.com/ |
| 298 | [ith]: https://en.wikipedia.org/wiki/Information_theory |
| 299 | [lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch |
| 300 | [prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator |
| 301 | [rs]: https://rsync.samba.org/ |
| 302 | [us]: http://www.cis.upenn.edu/~bcpierce/unison/ |
| 303 |
+7
-7
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -14,14 +14,14 @@ | ||
| 14 | 14 | <li> [./userlinks.wiki | User Links] |
| 15 | 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | 17 | <li> [./permutedindex.html | Doc Index] |
| 18 | 18 | </ul> |
| 19 | -<center><img src="fossil3.gif" align="center"></center> | |
| 19 | +<p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p> | |
| 20 | 20 | </div> |
| 21 | 21 | |
| 22 | -<p>Fossil is a simple, high-reliability, distributed software configuration | |
| 22 | +Fossil is a simple, high-reliability, distributed software configuration | |
| 23 | 23 | management system with these advanced features: |
| 24 | 24 | |
| 25 | 25 | 1. <b>Project Management</b> - |
| 26 | 26 | In addition to doing [./concepts.wiki | distributed version control] |
| 27 | 27 | like Git and Mercurial, |
| @@ -33,11 +33,11 @@ | ||
| 33 | 33 | 2. <b>Built-in Web Interface</b> - |
| 34 | 34 | Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible], |
| 35 | 35 | and intuitive [./webui.wiki | web interface] |
| 36 | 36 | with a rich variety of information pages |
| 37 | 37 | ([./webpage-ex.md|examples]) promoting situational awareness. |
| 38 | - <p> | |
| 38 | + <br><br> | |
| 39 | 39 | This entire website is just a running instance of Fossil. |
| 40 | 40 | The pages you see here are all [./wikitheory.wiki | wiki] or |
| 41 | 41 | [./embeddeddoc.wiki | embedded documentation] or (in the case of |
| 42 | 42 | the [/uv/download.html|download] page) |
| 43 | 43 | [./unvers.wiki | unversioned files]. |
| @@ -83,16 +83,16 @@ | ||
| 83 | 83 | the repository are consistent prior to each commit. |
| 84 | 84 | |
| 85 | 85 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | 86 | |
| 87 | 87 | <hr> |
| 88 | -<h3>Latest Release: 2.21 ([/timeline?c=version-2.21|2023-02-25])</h3> | |
| 88 | +<h3>Latest Release: 2.22 ([/timeline?c=version-2.22|2023-05-31])</h3> | |
| 89 | 89 | |
| 90 | 90 | * [/uv/download.html|Download] |
| 91 | - * [./changes.wiki#v2_21|Change Summary] | |
| 92 | - * [/timeline?p=version-2.21&bt=version-2.20&y=ci|Check-ins in version 2.21] | |
| 93 | - * [/timeline?df=version-2.21&y=ci|Check-ins derived from the 2.21 release] | |
| 91 | + * [./changes.wiki#v2_22|Change Summary] | |
| 92 | + * [/timeline?p=version-2.22&bt=version-2.21&y=ci|Check-ins in version 2.22] | |
| 93 | + * [/timeline?df=version-2.22&y=ci|Check-ins derived from the 2.22 release] | |
| 94 | 94 | * [/timeline?t=release|Timeline of all past releases] |
| 95 | 95 | |
| 96 | 96 | <hr> |
| 97 | 97 | <h3>Quick Start</h3> |
| 98 | 98 | |
| 99 | 99 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -14,14 +14,14 @@ | |
| 14 | <li> [./userlinks.wiki | User Links] |
| 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | <li> [./permutedindex.html | Doc Index] |
| 18 | </ul> |
| 19 | <center><img src="fossil3.gif" align="center"></center> |
| 20 | </div> |
| 21 | |
| 22 | <p>Fossil is a simple, high-reliability, distributed software configuration |
| 23 | management system with these advanced features: |
| 24 | |
| 25 | 1. <b>Project Management</b> - |
| 26 | In addition to doing [./concepts.wiki | distributed version control] |
| 27 | like Git and Mercurial, |
| @@ -33,11 +33,11 @@ | |
| 33 | 2. <b>Built-in Web Interface</b> - |
| 34 | Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible], |
| 35 | and intuitive [./webui.wiki | web interface] |
| 36 | with a rich variety of information pages |
| 37 | ([./webpage-ex.md|examples]) promoting situational awareness. |
| 38 | <p> |
| 39 | This entire website is just a running instance of Fossil. |
| 40 | The pages you see here are all [./wikitheory.wiki | wiki] or |
| 41 | [./embeddeddoc.wiki | embedded documentation] or (in the case of |
| 42 | the [/uv/download.html|download] page) |
| 43 | [./unvers.wiki | unversioned files]. |
| @@ -83,16 +83,16 @@ | |
| 83 | the repository are consistent prior to each commit. |
| 84 | |
| 85 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | |
| 87 | <hr> |
| 88 | <h3>Latest Release: 2.21 ([/timeline?c=version-2.21|2023-02-25])</h3> |
| 89 | |
| 90 | * [/uv/download.html|Download] |
| 91 | * [./changes.wiki#v2_21|Change Summary] |
| 92 | * [/timeline?p=version-2.21&bt=version-2.20&y=ci|Check-ins in version 2.21] |
| 93 | * [/timeline?df=version-2.21&y=ci|Check-ins derived from the 2.21 release] |
| 94 | * [/timeline?t=release|Timeline of all past releases] |
| 95 | |
| 96 | <hr> |
| 97 | <h3>Quick Start</h3> |
| 98 | |
| 99 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -14,14 +14,14 @@ | |
| 14 | <li> [./userlinks.wiki | User Links] |
| 15 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 16 | <li> [./fossil-v-git.wiki | Fossil vs. Git] |
| 17 | <li> [./permutedindex.html | Doc Index] |
| 18 | </ul> |
| 19 | <p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p> |
| 20 | </div> |
| 21 | |
| 22 | Fossil is a simple, high-reliability, distributed software configuration |
| 23 | management system with these advanced features: |
| 24 | |
| 25 | 1. <b>Project Management</b> - |
| 26 | In addition to doing [./concepts.wiki | distributed version control] |
| 27 | like Git and Mercurial, |
| @@ -33,11 +33,11 @@ | |
| 33 | 2. <b>Built-in Web Interface</b> - |
| 34 | Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible], |
| 35 | and intuitive [./webui.wiki | web interface] |
| 36 | with a rich variety of information pages |
| 37 | ([./webpage-ex.md|examples]) promoting situational awareness. |
| 38 | <br><br> |
| 39 | This entire website is just a running instance of Fossil. |
| 40 | The pages you see here are all [./wikitheory.wiki | wiki] or |
| 41 | [./embeddeddoc.wiki | embedded documentation] or (in the case of |
| 42 | the [/uv/download.html|download] page) |
| 43 | [./unvers.wiki | unversioned files]. |
| @@ -83,16 +83,16 @@ | |
| 83 | the repository are consistent prior to each commit. |
| 84 | |
| 85 | 8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 86 | |
| 87 | <hr> |
| 88 | <h3>Latest Release: 2.22 ([/timeline?c=version-2.22|2023-05-31])</h3> |
| 89 | |
| 90 | * [/uv/download.html|Download] |
| 91 | * [./changes.wiki#v2_22|Change Summary] |
| 92 | * [/timeline?p=version-2.22&bt=version-2.21&y=ci|Check-ins in version 2.22] |
| 93 | * [/timeline?df=version-2.22&y=ci|Check-ins derived from the 2.22 release] |
| 94 | * [/timeline?t=release|Timeline of all past releases] |
| 95 | |
| 96 | <hr> |
| 97 | <h3>Quick Start</h3> |
| 98 | |
| 99 |
+52
-59
| --- www/mirrortogithub.md | ||
| +++ www/mirrortogithub.md | ||
| @@ -2,83 +2,76 @@ | ||
| 2 | 2 | |
| 3 | 3 | Beginning with Fossil version 2.9, you can mirror a Fossil-based |
| 4 | 4 | project on GitHub (with [limitations](./mirrorlimitations.md)) |
| 5 | 5 | by following these steps: |
| 6 | 6 | |
| 7 | -<ol> | |
| 8 | -<li><p>Create an account on GitHub if you do not have one already. Log | |
| 7 | +1. Create an account on GitHub if you do not have one already. Log | |
| 9 | 8 | into that account. |
| 10 | 9 | |
| 11 | -<li><p>Create a new project. GitHub will ask you if you want to prepopulate | |
| 10 | +2. Create a new project. GitHub will ask you if you want to prepopulate | |
| 12 | 11 | your project with various things like a README file. Answer "no" to |
| 13 | 12 | everything. You want a completely blank project. GitHub will then |
| 14 | 13 | supply you with a URL for your project that will look something |
| 15 | 14 | like this: |
| 16 | 15 | |
| 17 | -<blockquote> | |
| 18 | -https://github.com/username/project.git | |
| 19 | -</blockquote> | |
| 16 | + https://github.com/username/project.git | |
| 20 | 17 | |
| 21 | -<li><p>Back on your workstation, move to a checkout for your Fossil | |
| 18 | +3. Back on your workstation, move to a checkout for your Fossil | |
| 22 | 19 | project and type: |
| 23 | 20 | |
| 24 | -<blockquote> | |
| 25 | -<pre>$ fossil git export /path/to/git/repo --autopush \ | |
| 26 | - https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git</pre> | |
| 27 | -</blockquote> | |
| 28 | - | |
| 29 | -<p> In place of the <code>/path/to...</code> argument above, put in | |
| 30 | - some directory name that is <i>outside</i> of your Fossil checkout. If | |
| 31 | - you keep multiple Fossil checkouts in a directory of their own, | |
| 32 | - consider using <code>../git-mirror</code> to place the Git export | |
| 33 | - mirror alongside them, for example. Fossil will create this | |
| 34 | - directory if necessary. This directory will become a Git | |
| 35 | - repository that holds a translation of your Fossil repository. | |
| 36 | - | |
| 37 | -<p> The <code>--autopush</code> option tells Fossil that you want to | |
| 38 | - push the Git translation up to GitHub every time it is updated. | |
| 39 | - | |
| 40 | -<p> The URL parameter is the same as the one GitHub gave you, but with | |
| 41 | - your GitHub <font color="orange">username</font> and <font | |
| 42 | - color="red">password</font> added. | |
| 43 | - | |
| 44 | -<p> If your GitHub account uses two-factor authentication (2FA), you | |
| 45 | - will have to <a href="https://github.com/settings/tokens">generate | |
| 46 | - a personal access token</a> and use that in place of your actual | |
| 47 | - password in the URL. This token should have “repo” scope enabled, | |
| 48 | - only. | |
| 49 | - | |
| 50 | -<p> You can also run the command above outside of any open checkout of | |
| 51 | - your project by supplying the “<code>-R repository</code>” | |
| 52 | - option. | |
| 53 | - | |
| 54 | -<li><p>Get some coffee. Depending on the size of your project, the | |
| 55 | - initial "<code>fossil git export</code>" command in the previous | |
| 56 | - step might run for several minutes. | |
| 57 | - | |
| 58 | -<li><p>And you are done! Assuming everything worked, your project is now | |
| 21 | + <blockquote> | |
| 22 | + <pre> | |
| 23 | + $ fossil git export /path/to/git/repo --autopush \ | |
| 24 | + https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git | |
| 25 | + </pre> | |
| 26 | + </blockquote> | |
| 27 | + | |
| 28 | + In place of the <code>/path/to...</code> argument above, put in | |
| 29 | + some directory name that is <i>outside</i> of your Fossil checkout. If | |
| 30 | + you keep multiple Fossil checkouts in a directory of their own, | |
| 31 | + consider using <code>../git-mirror</code> to place the Git export | |
| 32 | + mirror alongside them, for example. Fossil will create this | |
| 33 | + directory if necessary. This directory will become a Git | |
| 34 | + repository that holds a translation of your Fossil repository. | |
| 35 | + | |
| 36 | + The <code>--autopush</code> option tells Fossil that you want to | |
| 37 | + push the Git translation up to GitHub every time it is updated. | |
| 38 | + | |
| 39 | + The URL parameter is the same as the one GitHub gave you, but with | |
| 40 | + your GitHub <font color="orange">username</font> and <font | |
| 41 | + color="red">password</font> added. | |
| 42 | + | |
| 43 | + If your GitHub account uses two-factor authentication (2FA), you | |
| 44 | + will have to <a href="https://github.com/settings/tokens">generate | |
| 45 | + a personal access token</a> and use that in place of your actual | |
| 46 | + password in the URL. This token should have “repo” scope enabled, | |
| 47 | + only. | |
| 48 | + | |
| 49 | + You can also run the command above outside of any open checkout of | |
| 50 | + your project by supplying the “<code>-R repository</code>” | |
| 51 | + option. | |
| 52 | + | |
| 53 | +4. Get some coffee. Depending on the size of your project, the | |
| 54 | + initial "<code>fossil git export</code>" command in the previous | |
| 55 | + step might run for several minutes. | |
| 56 | + | |
| 57 | +5. And you are done! Assuming everything worked, your project is now | |
| 59 | 58 | mirrored on GitHub. |
| 60 | 59 | |
| 61 | -<li><p>Whenever you update your project, simply run this command to update | |
| 60 | +6. Whenever you update your project, simply run this command to update | |
| 62 | 61 | the mirror: |
| 63 | 62 | |
| 64 | -<blockquote> | |
| 65 | -<pre>$ fossil git export</pre> | |
| 66 | -</blockquote> | |
| 67 | - | |
| 68 | - | |
| 69 | -<p> Unlike with the first time you ran that command, you don’t need | |
| 70 | - the remaining arguments, because Fossil remembers those things. | |
| 71 | - Subsequent mirror updates should usually happen in a fraction of | |
| 72 | - a second. | |
| 73 | - | |
| 74 | -<li><p>To see the status of your mirror, run: | |
| 75 | - | |
| 76 | -<blockquote> | |
| 77 | -<pre>$ fossil git status</pre> | |
| 78 | -</blockquote> | |
| 79 | -</ol> | |
| 63 | + $ fossil git export | |
| 64 | + | |
| 65 | + Unlike with the first time you ran that command, you don’t need | |
| 66 | + the remaining arguments, because Fossil remembers those things. | |
| 67 | + Subsequent mirror updates should usually happen in a fraction of | |
| 68 | + a second. | |
| 69 | + | |
| 70 | +7. To see the status of your mirror, run: | |
| 71 | + | |
| 72 | + $ fossil git status | |
| 80 | 73 | |
| 81 | 74 | ## Notes: |
| 82 | 75 | |
| 83 | 76 | * Unless you specify --force, the mirroring only happens if the Fossil |
| 84 | 77 | repo has changed, with Fossil reporting "no changes", because Fossil |
| 85 | 78 |
| --- www/mirrortogithub.md | |
| +++ www/mirrortogithub.md | |
| @@ -2,83 +2,76 @@ | |
| 2 | |
| 3 | Beginning with Fossil version 2.9, you can mirror a Fossil-based |
| 4 | project on GitHub (with [limitations](./mirrorlimitations.md)) |
| 5 | by following these steps: |
| 6 | |
| 7 | <ol> |
| 8 | <li><p>Create an account on GitHub if you do not have one already. Log |
| 9 | into that account. |
| 10 | |
| 11 | <li><p>Create a new project. GitHub will ask you if you want to prepopulate |
| 12 | your project with various things like a README file. Answer "no" to |
| 13 | everything. You want a completely blank project. GitHub will then |
| 14 | supply you with a URL for your project that will look something |
| 15 | like this: |
| 16 | |
| 17 | <blockquote> |
| 18 | https://github.com/username/project.git |
| 19 | </blockquote> |
| 20 | |
| 21 | <li><p>Back on your workstation, move to a checkout for your Fossil |
| 22 | project and type: |
| 23 | |
| 24 | <blockquote> |
| 25 | <pre>$ fossil git export /path/to/git/repo --autopush \ |
| 26 | https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git</pre> |
| 27 | </blockquote> |
| 28 | |
| 29 | <p> In place of the <code>/path/to...</code> argument above, put in |
| 30 | some directory name that is <i>outside</i> of your Fossil checkout. If |
| 31 | you keep multiple Fossil checkouts in a directory of their own, |
| 32 | consider using <code>../git-mirror</code> to place the Git export |
| 33 | mirror alongside them, for example. Fossil will create this |
| 34 | directory if necessary. This directory will become a Git |
| 35 | repository that holds a translation of your Fossil repository. |
| 36 | |
| 37 | <p> The <code>--autopush</code> option tells Fossil that you want to |
| 38 | push the Git translation up to GitHub every time it is updated. |
| 39 | |
| 40 | <p> The URL parameter is the same as the one GitHub gave you, but with |
| 41 | your GitHub <font color="orange">username</font> and <font |
| 42 | color="red">password</font> added. |
| 43 | |
| 44 | <p> If your GitHub account uses two-factor authentication (2FA), you |
| 45 | will have to <a href="https://github.com/settings/tokens">generate |
| 46 | a personal access token</a> and use that in place of your actual |
| 47 | password in the URL. This token should have “repo” scope enabled, |
| 48 | only. |
| 49 | |
| 50 | <p> You can also run the command above outside of any open checkout of |
| 51 | your project by supplying the “<code>-R repository</code>” |
| 52 | option. |
| 53 | |
| 54 | <li><p>Get some coffee. Depending on the size of your project, the |
| 55 | initial "<code>fossil git export</code>" command in the previous |
| 56 | step might run for several minutes. |
| 57 | |
| 58 | <li><p>And you are done! Assuming everything worked, your project is now |
| 59 | mirrored on GitHub. |
| 60 | |
| 61 | <li><p>Whenever you update your project, simply run this command to update |
| 62 | the mirror: |
| 63 | |
| 64 | <blockquote> |
| 65 | <pre>$ fossil git export</pre> |
| 66 | </blockquote> |
| 67 | |
| 68 | |
| 69 | <p> Unlike with the first time you ran that command, you don’t need |
| 70 | the remaining arguments, because Fossil remembers those things. |
| 71 | Subsequent mirror updates should usually happen in a fraction of |
| 72 | a second. |
| 73 | |
| 74 | <li><p>To see the status of your mirror, run: |
| 75 | |
| 76 | <blockquote> |
| 77 | <pre>$ fossil git status</pre> |
| 78 | </blockquote> |
| 79 | </ol> |
| 80 | |
| 81 | ## Notes: |
| 82 | |
| 83 | * Unless you specify --force, the mirroring only happens if the Fossil |
| 84 | repo has changed, with Fossil reporting "no changes", because Fossil |
| 85 |
| --- www/mirrortogithub.md | |
| +++ www/mirrortogithub.md | |
| @@ -2,83 +2,76 @@ | |
| 2 | |
| 3 | Beginning with Fossil version 2.9, you can mirror a Fossil-based |
| 4 | project on GitHub (with [limitations](./mirrorlimitations.md)) |
| 5 | by following these steps: |
| 6 | |
| 7 | 1. Create an account on GitHub if you do not have one already. Log |
| 8 | into that account. |
| 9 | |
| 10 | 2. Create a new project. GitHub will ask you if you want to prepopulate |
| 11 | your project with various things like a README file. Answer "no" to |
| 12 | everything. You want a completely blank project. GitHub will then |
| 13 | supply you with a URL for your project that will look something |
| 14 | like this: |
| 15 | |
| 16 | https://github.com/username/project.git |
| 17 | |
| 18 | 3. Back on your workstation, move to a checkout for your Fossil |
| 19 | project and type: |
| 20 | |
| 21 | <blockquote> |
| 22 | <pre> |
| 23 | $ fossil git export /path/to/git/repo --autopush \ |
| 24 | https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git |
| 25 | </pre> |
| 26 | </blockquote> |
| 27 | |
| 28 | In place of the <code>/path/to...</code> argument above, put in |
| 29 | some directory name that is <i>outside</i> of your Fossil checkout. If |
| 30 | you keep multiple Fossil checkouts in a directory of their own, |
| 31 | consider using <code>../git-mirror</code> to place the Git export |
| 32 | mirror alongside them, for example. Fossil will create this |
| 33 | directory if necessary. This directory will become a Git |
| 34 | repository that holds a translation of your Fossil repository. |
| 35 | |
| 36 | The <code>--autopush</code> option tells Fossil that you want to |
| 37 | push the Git translation up to GitHub every time it is updated. |
| 38 | |
| 39 | The URL parameter is the same as the one GitHub gave you, but with |
| 40 | your GitHub <font color="orange">username</font> and <font |
| 41 | color="red">password</font> added. |
| 42 | |
| 43 | If your GitHub account uses two-factor authentication (2FA), you |
| 44 | will have to <a href="https://github.com/settings/tokens">generate |
| 45 | a personal access token</a> and use that in place of your actual |
| 46 | password in the URL. This token should have “repo” scope enabled, |
| 47 | only. |
| 48 | |
| 49 | You can also run the command above outside of any open checkout of |
| 50 | your project by supplying the “<code>-R repository</code>” |
| 51 | option. |
| 52 | |
| 53 | 4. Get some coffee. Depending on the size of your project, the |
| 54 | initial "<code>fossil git export</code>" command in the previous |
| 55 | step might run for several minutes. |
| 56 | |
| 57 | 5. And you are done! Assuming everything worked, your project is now |
| 58 | mirrored on GitHub. |
| 59 | |
| 60 | 6. Whenever you update your project, simply run this command to update |
| 61 | the mirror: |
| 62 | |
| 63 | $ fossil git export |
| 64 | |
| 65 | Unlike with the first time you ran that command, you don’t need |
| 66 | the remaining arguments, because Fossil remembers those things. |
| 67 | Subsequent mirror updates should usually happen in a fraction of |
| 68 | a second. |
| 69 | |
| 70 | 7. To see the status of your mirror, run: |
| 71 | |
| 72 | $ fossil git status |
| 73 | |
| 74 | ## Notes: |
| 75 | |
| 76 | * Unless you specify --force, the mirroring only happens if the Fossil |
| 77 | repo has changed, with Fossil reporting "no changes", because Fossil |
| 78 |
+1
-3
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -153,16 +153,14 @@ | ||
| 153 | 153 | set out [open permutedindex.html w] |
| 154 | 154 | fconfigure $out -encoding utf-8 -translation lf |
| 155 | 155 | puts $out \ |
| 156 | 156 | "<div class='fossil-doc' data-title='Index Of Fossil Documentation'>" |
| 157 | 157 | puts $out { |
| 158 | -<center> | |
| 159 | -<form action='$ROOT/docsrch' method='GET'> | |
| 158 | +<form action='$ROOT/docsrch' method='GET' style="text-align:center"> | |
| 160 | 159 | <input type="text" name="s" size="40" autofocus> |
| 161 | 160 | <input type="submit" value="Search Docs"> |
| 162 | 161 | </form> |
| 163 | -</center> | |
| 164 | 162 | <h2>Primary Documents:</h2> |
| 165 | 163 | <ul> |
| 166 | 164 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 167 | 165 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 168 | 166 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 169 | 167 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -153,16 +153,14 @@ | |
| 153 | set out [open permutedindex.html w] |
| 154 | fconfigure $out -encoding utf-8 -translation lf |
| 155 | puts $out \ |
| 156 | "<div class='fossil-doc' data-title='Index Of Fossil Documentation'>" |
| 157 | puts $out { |
| 158 | <center> |
| 159 | <form action='$ROOT/docsrch' method='GET'> |
| 160 | <input type="text" name="s" size="40" autofocus> |
| 161 | <input type="submit" value="Search Docs"> |
| 162 | </form> |
| 163 | </center> |
| 164 | <h2>Primary Documents:</h2> |
| 165 | <ul> |
| 166 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 167 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 168 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 169 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -153,16 +153,14 @@ | |
| 153 | set out [open permutedindex.html w] |
| 154 | fconfigure $out -encoding utf-8 -translation lf |
| 155 | puts $out \ |
| 156 | "<div class='fossil-doc' data-title='Index Of Fossil Documentation'>" |
| 157 | puts $out { |
| 158 | <form action='$ROOT/docsrch' method='GET' style="text-align:center"> |
| 159 | <input type="text" name="s" size="40" autofocus> |
| 160 | <input type="submit" value="Search Docs"> |
| 161 | </form> |
| 162 | <h2>Primary Documents:</h2> |
| 163 | <ul> |
| 164 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 165 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 166 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 167 |
+2
-2
| --- www/newrepo.wiki | ||
| +++ www/newrepo.wiki | ||
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | <title>How To Create A New Fossil Repository</title> |
| 2 | 2 | |
| 3 | -<p> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how | |
| 3 | +The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how | |
| 4 | 4 | to get up and running with fossil. But once you're running, what can |
| 5 | 5 | you do with it? This document will walk you through the process of |
| 6 | 6 | creating a fossil repository, populating it with files, and then |
| 7 | -sharing it over the web.</p> | |
| 7 | +sharing it over the web. | |
| 8 | 8 | |
| 9 | 9 | The first thing we need to do is create a fossil repository file: |
| 10 | 10 | |
| 11 | 11 | <verbatim> |
| 12 | 12 | stephan@ludo:~/fossil$ fossil new demo.fossil |
| 13 | 13 |
| --- www/newrepo.wiki | |
| +++ www/newrepo.wiki | |
| @@ -1,12 +1,12 @@ | |
| 1 | <title>How To Create A New Fossil Repository</title> |
| 2 | |
| 3 | <p> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how |
| 4 | to get up and running with fossil. But once you're running, what can |
| 5 | you do with it? This document will walk you through the process of |
| 6 | creating a fossil repository, populating it with files, and then |
| 7 | sharing it over the web.</p> |
| 8 | |
| 9 | The first thing we need to do is create a fossil repository file: |
| 10 | |
| 11 | <verbatim> |
| 12 | stephan@ludo:~/fossil$ fossil new demo.fossil |
| 13 |
| --- www/newrepo.wiki | |
| +++ www/newrepo.wiki | |
| @@ -1,12 +1,12 @@ | |
| 1 | <title>How To Create A New Fossil Repository</title> |
| 2 | |
| 3 | The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how |
| 4 | to get up and running with fossil. But once you're running, what can |
| 5 | you do with it? This document will walk you through the process of |
| 6 | creating a fossil repository, populating it with files, and then |
| 7 | sharing it over the web. |
| 8 | |
| 9 | The first thing we need to do is create a fossil repository file: |
| 10 | |
| 11 | <verbatim> |
| 12 | stephan@ludo:~/fossil$ fossil new demo.fossil |
| 13 |
+1
-3
| --- www/permutedindex.html | ||
| +++ www/permutedindex.html | ||
| @@ -1,13 +1,11 @@ | ||
| 1 | 1 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> |
| 2 | 2 | |
| 3 | -<center> | |
| 4 | -<form action='$ROOT/docsrch' method='GET'> | |
| 3 | +<form action='$ROOT/docsrch' method='GET' style="text-align:center"> | |
| 5 | 4 | <input type="text" name="s" size="40" autofocus> |
| 6 | 5 | <input type="submit" value="Search Docs"> |
| 7 | 6 | </form> |
| 8 | -</center> | |
| 9 | 7 | <h2>Primary Documents:</h2> |
| 10 | 8 | <ul> |
| 11 | 9 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 12 | 10 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 13 | 11 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 | 12 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -1,13 +1,11 @@ | |
| 1 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> |
| 2 | |
| 3 | <center> |
| 4 | <form action='$ROOT/docsrch' method='GET'> |
| 5 | <input type="text" name="s" size="40" autofocus> |
| 6 | <input type="submit" value="Search Docs"> |
| 7 | </form> |
| 8 | </center> |
| 9 | <h2>Primary Documents:</h2> |
| 10 | <ul> |
| 11 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 12 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 13 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 14 |
| --- www/permutedindex.html | |
| +++ www/permutedindex.html | |
| @@ -1,13 +1,11 @@ | |
| 1 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> |
| 2 | |
| 3 | <form action='$ROOT/docsrch' method='GET' style="text-align:center"> |
| 4 | <input type="text" name="s" size="40" autofocus> |
| 5 | <input type="submit" value="Search Docs"> |
| 6 | </form> |
| 7 | <h2>Primary Documents:</h2> |
| 8 | <ul> |
| 9 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> |
| 10 | <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> |
| 11 | <li> <a href='history.md'>Purpose and History of Fossil</a> |
| 12 |
+68
-71
| --- www/pop.wiki | ||
| +++ www/pop.wiki | ||
| @@ -1,76 +1,73 @@ | ||
| 1 | 1 | <title>Principles Of Operation</title> |
| 2 | 2 | <h1 align="center">Principles Of Operation</h1> |
| 3 | 3 | |
| 4 | -<p> | |
| 5 | 4 | This page attempts to define the foundational principals upon |
| 6 | 5 | which Fossil is built. |
| 7 | -</p> | |
| 8 | - | |
| 9 | -<ul> | |
| 10 | -<li><p>A project consists of source files, wiki pages, and | |
| 11 | -trouble tickets, and control files (collectively "artifacts"). | |
| 12 | -All historical copies of all artifacts | |
| 13 | -are saved. The project maintains an audit | |
| 14 | -trail.</p></li> | |
| 15 | - | |
| 16 | -<li><p>A project resides in one or more repositories. Each | |
| 17 | -repository is administered and operates independently | |
| 18 | -of the others.</p></li> | |
| 19 | - | |
| 20 | -<li><p>Each repository has both global and local state. The | |
| 21 | -global state is common to all repositories (or at least | |
| 22 | -has the potential to be shared in common when the | |
| 23 | -repositories are fully synchronized). The local state | |
| 24 | -for each repository is private to that repository. | |
| 25 | -The global state represents the content of the project. | |
| 26 | -The local state identifies the authorized users and | |
| 27 | -access policies for a particular repository.</p></li> | |
| 28 | - | |
| 29 | -<li><p>The global state of a repository is an unordered | |
| 30 | -collection of artifacts. Each artifact is named by a | |
| 31 | -cryptographic hash (SHA1 or SHA3-256) encoded in | |
| 32 | -lowercase hexadecimal. | |
| 33 | -In many contexts, the name can be | |
| 34 | -abbreviated to a unique prefix. A five- or six-character | |
| 35 | -prefix usually suffices to uniquely identify a file.</p></li> | |
| 36 | - | |
| 37 | -<li><p>Because artifacts are named by a cryptographic hash, all artifacts | |
| 38 | -are immutable. Any change to the content of an artifact also | |
| 39 | -changes the hash that forms the artifacts name, thus | |
| 40 | -creating a new artifact. Both the old original version of the | |
| 41 | -artifact and the new change are preserved under different names.</p></li> | |
| 42 | - | |
| 43 | -<li><p>It is theoretically possible for two artifacts with different | |
| 44 | -content to share the same hash. But finding two such | |
| 45 | -artifacts is so incredibly difficult and unlikely that we | |
| 46 | -consider it to be an impossibility.</p></li> | |
| 47 | - | |
| 48 | -<li><p>The signature of an artifact is the cryptographic hash of the | |
| 49 | -artifact itself, exactly as it would appear in a disk file. No prefix | |
| 50 | -or meta-information about the artifact is added before computing | |
| 51 | -the hash. So you can | |
| 52 | -always find the signature of a file by using the | |
| 53 | -"sha1sum" or "sha3sum" or similar command-line utilities.</p></li> | |
| 54 | - | |
| 55 | -<li><p>The artifacts that comprise the global state of a repository | |
| 56 | -are the complete global state of that repository. The SQLite | |
| 57 | -database that holds the repository contains additional information | |
| 58 | -about linkages between artifacts, but all of that added information | |
| 59 | -can be discarded and reconstructed by rescanning the content | |
| 60 | -artifacts.</p></li> | |
| 61 | - | |
| 62 | -<li><p>Two repositories for the same project can synchronize | |
| 63 | -their global states simply by sharing artifacts. The local | |
| 64 | -state of repositories is not normally synchronized or | |
| 65 | -shared.</p></li> | |
| 66 | - | |
| 67 | -<li><p>Every check-in has a special file at the top-level | |
| 68 | -named "manifest" which is an index of all other files in | |
| 69 | -that check-in. The manifest is automatically created and | |
| 70 | -maintained by the system.</p></li> | |
| 71 | - | |
| 72 | -<li><p>The <a href="fileformat.wiki">file formats</a> | |
| 73 | -used by Fossil are all very simple so that with access | |
| 74 | -to the original content files, one can easily reconstruct | |
| 75 | -the content of a check-in without the need for any | |
| 76 | -special tools or software.</p></li> | |
| 6 | + | |
| 7 | + * A project consists of source files, wiki pages, and | |
| 8 | + trouble tickets, and control files (collectively "artifacts"). | |
| 9 | + All historical copies of all artifacts | |
| 10 | + are saved. The project maintains an audit | |
| 11 | + trail. | |
| 12 | + | |
| 13 | + * A project resides in one or more repositories. Each | |
| 14 | + repository is administered and operates independently | |
| 15 | + of the others. | |
| 16 | + | |
| 17 | + * Each repository has both global and local state. The | |
| 18 | + global state is common to all repositories (or at least | |
| 19 | + has the potential to be shared in common when the | |
| 20 | + repositories are fully synchronized). The local state | |
| 21 | + for each repository is private to that repository. | |
| 22 | + The global state represents the content of the project. | |
| 23 | + The local state identifies the authorized users and | |
| 24 | + access policies for a particular repository. | |
| 25 | + | |
| 26 | + * The global state of a repository is an unordered | |
| 27 | + collection of artifacts. Each artifact is named by a | |
| 28 | + cryptographic hash (SHA1 or SHA3-256) encoded in | |
| 29 | + lowercase hexadecimal. | |
| 30 | + In many contexts, the name can be | |
| 31 | + abbreviated to a unique prefix. A five- or six-character | |
| 32 | + prefix usually suffices to uniquely identify a file. | |
| 33 | + | |
| 34 | + * Because artifacts are named by a cryptographic hash, all artifacts | |
| 35 | + are immutable. Any change to the content of an artifact also | |
| 36 | + changes the hash that forms the artifacts name, thus | |
| 37 | + creating a new artifact. Both the old original version of the | |
| 38 | + artifact and the new change are preserved under different names. | |
| 39 | + | |
| 40 | + * It is theoretically possible for two artifacts with different | |
| 41 | + content to share the same hash. But finding two such | |
| 42 | + artifacts is so incredibly difficult and unlikely that we | |
| 43 | + consider it to be an impossibility. | |
| 44 | + | |
| 45 | + * The signature of an artifact is the cryptographic hash of the | |
| 46 | + artifact itself, exactly as it would appear in a disk file. No prefix | |
| 47 | + or meta-information about the artifact is added before computing | |
| 48 | + the hash. So you can | |
| 49 | + always find the signature of a file by using the | |
| 50 | + "sha1sum" or "sha3sum" or similar command-line utilities. | |
| 51 | + | |
| 52 | + * The artifacts that comprise the global state of a repository | |
| 53 | + are the complete global state of that repository. The SQLite | |
| 54 | + database that holds the repository contains additional information | |
| 55 | + about linkages between artifacts, but all of that added information | |
| 56 | + can be discarded and reconstructed by rescanning the content | |
| 57 | + artifacts. | |
| 58 | + | |
| 59 | + * Two repositories for the same project can synchronize | |
| 60 | + their global states simply by sharing artifacts. The local | |
| 61 | + state of repositories is not normally synchronized or | |
| 62 | + shared. | |
| 63 | + | |
| 64 | + * Every check-in has a special file at the top-level | |
| 65 | + named "manifest" which is an index of all other files in | |
| 66 | + that check-in. The manifest is automatically created and | |
| 67 | + maintained by the system. | |
| 68 | + | |
| 69 | + * The <a href="fileformat.wiki">file formats</a> | |
| 70 | + used by Fossil are all very simple so that with access | |
| 71 | + to the original content files, one can easily reconstruct | |
| 72 | + the content of a check-in without the need for any | |
| 73 | + special tools or software. | |
| 77 | 74 |
| --- www/pop.wiki | |
| +++ www/pop.wiki | |
| @@ -1,76 +1,73 @@ | |
| 1 | <title>Principles Of Operation</title> |
| 2 | <h1 align="center">Principles Of Operation</h1> |
| 3 | |
| 4 | <p> |
| 5 | This page attempts to define the foundational principals upon |
| 6 | which Fossil is built. |
| 7 | </p> |
| 8 | |
| 9 | <ul> |
| 10 | <li><p>A project consists of source files, wiki pages, and |
| 11 | trouble tickets, and control files (collectively "artifacts"). |
| 12 | All historical copies of all artifacts |
| 13 | are saved. The project maintains an audit |
| 14 | trail.</p></li> |
| 15 | |
| 16 | <li><p>A project resides in one or more repositories. Each |
| 17 | repository is administered and operates independently |
| 18 | of the others.</p></li> |
| 19 | |
| 20 | <li><p>Each repository has both global and local state. The |
| 21 | global state is common to all repositories (or at least |
| 22 | has the potential to be shared in common when the |
| 23 | repositories are fully synchronized). The local state |
| 24 | for each repository is private to that repository. |
| 25 | The global state represents the content of the project. |
| 26 | The local state identifies the authorized users and |
| 27 | access policies for a particular repository.</p></li> |
| 28 | |
| 29 | <li><p>The global state of a repository is an unordered |
| 30 | collection of artifacts. Each artifact is named by a |
| 31 | cryptographic hash (SHA1 or SHA3-256) encoded in |
| 32 | lowercase hexadecimal. |
| 33 | In many contexts, the name can be |
| 34 | abbreviated to a unique prefix. A five- or six-character |
| 35 | prefix usually suffices to uniquely identify a file.</p></li> |
| 36 | |
| 37 | <li><p>Because artifacts are named by a cryptographic hash, all artifacts |
| 38 | are immutable. Any change to the content of an artifact also |
| 39 | changes the hash that forms the artifacts name, thus |
| 40 | creating a new artifact. Both the old original version of the |
| 41 | artifact and the new change are preserved under different names.</p></li> |
| 42 | |
| 43 | <li><p>It is theoretically possible for two artifacts with different |
| 44 | content to share the same hash. But finding two such |
| 45 | artifacts is so incredibly difficult and unlikely that we |
| 46 | consider it to be an impossibility.</p></li> |
| 47 | |
| 48 | <li><p>The signature of an artifact is the cryptographic hash of the |
| 49 | artifact itself, exactly as it would appear in a disk file. No prefix |
| 50 | or meta-information about the artifact is added before computing |
| 51 | the hash. So you can |
| 52 | always find the signature of a file by using the |
| 53 | "sha1sum" or "sha3sum" or similar command-line utilities.</p></li> |
| 54 | |
| 55 | <li><p>The artifacts that comprise the global state of a repository |
| 56 | are the complete global state of that repository. The SQLite |
| 57 | database that holds the repository contains additional information |
| 58 | about linkages between artifacts, but all of that added information |
| 59 | can be discarded and reconstructed by rescanning the content |
| 60 | artifacts.</p></li> |
| 61 | |
| 62 | <li><p>Two repositories for the same project can synchronize |
| 63 | their global states simply by sharing artifacts. The local |
| 64 | state of repositories is not normally synchronized or |
| 65 | shared.</p></li> |
| 66 | |
| 67 | <li><p>Every check-in has a special file at the top-level |
| 68 | named "manifest" which is an index of all other files in |
| 69 | that check-in. The manifest is automatically created and |
| 70 | maintained by the system.</p></li> |
| 71 | |
| 72 | <li><p>The <a href="fileformat.wiki">file formats</a> |
| 73 | used by Fossil are all very simple so that with access |
| 74 | to the original content files, one can easily reconstruct |
| 75 | the content of a check-in without the need for any |
| 76 | special tools or software.</p></li> |
| 77 |
| --- www/pop.wiki | |
| +++ www/pop.wiki | |
| @@ -1,76 +1,73 @@ | |
| 1 | <title>Principles Of Operation</title> |
| 2 | <h1 align="center">Principles Of Operation</h1> |
| 3 | |
| 4 | This page attempts to define the foundational principals upon |
| 5 | which Fossil is built. |
| 6 | |
| 7 | * A project consists of source files, wiki pages, and |
| 8 | trouble tickets, and control files (collectively "artifacts"). |
| 9 | All historical copies of all artifacts |
| 10 | are saved. The project maintains an audit |
| 11 | trail. |
| 12 | |
| 13 | * A project resides in one or more repositories. Each |
| 14 | repository is administered and operates independently |
| 15 | of the others. |
| 16 | |
| 17 | * Each repository has both global and local state. The |
| 18 | global state is common to all repositories (or at least |
| 19 | has the potential to be shared in common when the |
| 20 | repositories are fully synchronized). The local state |
| 21 | for each repository is private to that repository. |
| 22 | The global state represents the content of the project. |
| 23 | The local state identifies the authorized users and |
| 24 | access policies for a particular repository. |
| 25 | |
| 26 | * The global state of a repository is an unordered |
| 27 | collection of artifacts. Each artifact is named by a |
| 28 | cryptographic hash (SHA1 or SHA3-256) encoded in |
| 29 | lowercase hexadecimal. |
| 30 | In many contexts, the name can be |
| 31 | abbreviated to a unique prefix. A five- or six-character |
| 32 | prefix usually suffices to uniquely identify a file. |
| 33 | |
| 34 | * Because artifacts are named by a cryptographic hash, all artifacts |
| 35 | are immutable. Any change to the content of an artifact also |
| 36 | changes the hash that forms the artifacts name, thus |
| 37 | creating a new artifact. Both the old original version of the |
| 38 | artifact and the new change are preserved under different names. |
| 39 | |
| 40 | * It is theoretically possible for two artifacts with different |
| 41 | content to share the same hash. But finding two such |
| 42 | artifacts is so incredibly difficult and unlikely that we |
| 43 | consider it to be an impossibility. |
| 44 | |
| 45 | * The signature of an artifact is the cryptographic hash of the |
| 46 | artifact itself, exactly as it would appear in a disk file. No prefix |
| 47 | or meta-information about the artifact is added before computing |
| 48 | the hash. So you can |
| 49 | always find the signature of a file by using the |
| 50 | "sha1sum" or "sha3sum" or similar command-line utilities. |
| 51 | |
| 52 | * The artifacts that comprise the global state of a repository |
| 53 | are the complete global state of that repository. The SQLite |
| 54 | database that holds the repository contains additional information |
| 55 | about linkages between artifacts, but all of that added information |
| 56 | can be discarded and reconstructed by rescanning the content |
| 57 | artifacts. |
| 58 | |
| 59 | * Two repositories for the same project can synchronize |
| 60 | their global states simply by sharing artifacts. The local |
| 61 | state of repositories is not normally synchronized or |
| 62 | shared. |
| 63 | |
| 64 | * Every check-in has a special file at the top-level |
| 65 | named "manifest" which is an index of all other files in |
| 66 | that check-in. The manifest is automatically created and |
| 67 | maintained by the system. |
| 68 | |
| 69 | * The <a href="fileformat.wiki">file formats</a> |
| 70 | used by Fossil are all very simple so that with access |
| 71 | to the original content files, one can easily reconstruct |
| 72 | the content of a check-in without the need for any |
| 73 | special tools or software. |
| 74 |
+18
-18
| --- www/qandc.wiki | ||
| +++ www/qandc.wiki | ||
| @@ -1,25 +1,25 @@ | ||
| 1 | 1 | <title>Questions And Criticisms</title> |
| 2 | 2 | <nowiki> |
| 3 | 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | 4 | |
| 5 | -<p>This page is a collection of real questions and criticisms that were | |
| 5 | +This page is a collection of real questions and criticisms that were | |
| 6 | 6 | raised against Fossil early in its history (circa 2008). |
| 7 | 7 | This page is old and has not been kept up-to-date. See the |
| 8 | -</nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p> | |
| 8 | +</nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>. | |
| 9 | 9 | |
| 10 | 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | 12 | |
| 13 | 13 | <blockquote> |
| 14 | - <p>I wrote fossil because none of the | |
| 14 | + I wrote fossil because none of the | |
| 15 | 15 | other available DVCSes met my needs. If the other DVCSes do |
| 16 | 16 | meet your needs, then you might not need fossil. But they |
| 17 | - don't meet mine, and so fossil is necessary for me.</p> | |
| 17 | + don't meet mine, and so fossil is necessary for me. | |
| 18 | 18 | |
| 19 | - <p>Features provided by fossil that one does not get with other | |
| 20 | - DVCSes include:</p> | |
| 19 | + Features provided by fossil that one does not get with other | |
| 20 | + DVCSes include: | |
| 21 | 21 | |
| 22 | 22 | <ol> |
| 23 | 23 | <li> Integrated <a href="wikitheory.wiki">wiki</a>. </li> |
| 24 | 24 | <li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li> |
| 25 | 25 | <li> Immutable artifacts </li> |
| @@ -67,30 +67,30 @@ | ||
| 67 | 67 | |
| 68 | 68 | <b>Fossil looks like the bug tracker that would be in your |
| 69 | 69 | Linksys Router's administration screen.</b> |
| 70 | 70 | |
| 71 | 71 | <blockquote> |
| 72 | -<p>I take a pragmatic approach to software: form follows function. | |
| 72 | +I take a pragmatic approach to software: form follows function. | |
| 73 | 73 | To me, it is more important to have a reliable, fast, efficient, |
| 74 | -enduring, and simple DVCS than one that looks pretty.</p> | |
| 74 | +enduring, and simple DVCS than one that looks pretty. | |
| 75 | 75 | |
| 76 | -<p>On the other hand, if you have patches that improve the appearance | |
| 76 | +On the other hand, if you have patches that improve the appearance | |
| 77 | 77 | of Fossil without seriously compromising its reliability, performance, |
| 78 | 78 | and/or maintainability, I will be happy to accept them. Fossil is |
| 79 | 79 | self-hosting. Send email to request a password that will let |
| 80 | -you push to the main fossil repository.</p> | |
| 80 | +you push to the main fossil repository. | |
| 81 | 81 | </blockquote> |
| 82 | 82 | |
| 83 | 83 | <b>It would be useful to have a separate application that |
| 84 | 84 | keeps the bug-tracking database in a versioned file. That file can |
| 85 | 85 | then be pushed and pulled along with the rest repository.</b> |
| 86 | 86 | |
| 87 | 87 | <blockquote> |
| 88 | -<p>Fossil already <u>does</u> push and pull bugs along with the files | |
| 88 | +Fossil already <u>does</u> push and pull bugs along with the files | |
| 89 | 89 | in your repository. |
| 90 | 90 | But fossil does <u>not</u> track bugs as files in the source tree. |
| 91 | -That approach to bug tracking was rejected for three reasons:</p> | |
| 91 | +That approach to bug tracking was rejected for three reasons: | |
| 92 | 92 | |
| 93 | 93 | <ol> |
| 94 | 94 | <li> Check-ins in fossil are immutable. So if |
| 95 | 95 | tickets were part of the check-in, then there would be no way to add |
| 96 | 96 | new tickets to a check-in as new bugs are discovered. |
| @@ -105,12 +105,12 @@ | ||
| 105 | 105 | of tickets to developers with check-in privileges and an installed |
| 106 | 106 | copy of the fossil executable. Casual passers-by on the internet should |
| 107 | 107 | be permitted to create tickets. |
| 108 | 108 | </ol> |
| 109 | 109 | |
| 110 | -<p>These points are reiterated in the opening paragraphs of | |
| 111 | -the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.</p> | |
| 110 | +These points are reiterated in the opening paragraphs of | |
| 111 | +the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document. | |
| 112 | 112 | </blockquote> |
| 113 | 113 | |
| 114 | 114 | <b>Fossil is already the name of a plan9 versioned |
| 115 | 115 | append-only filesystem.</b> |
| 116 | 116 | |
| @@ -136,24 +136,24 @@ | ||
| 136 | 136 | directly in the VCS - either they are under-featured compared to full |
| 137 | 137 | software like Trac, or the VCS is massively bloated compared to |
| 138 | 138 | Subversion or Bazaar.</b> |
| 139 | 139 | |
| 140 | 140 | <blockquote> |
| 141 | -<p>I have no doubt that Trac has many features that fossil lacks. But that | |
| 141 | +I have no doubt that Trac has many features that fossil lacks. But that | |
| 142 | 142 | is not the point. Fossil has several key features that Trac lacks and that |
| 143 | 143 | I need: most notably the fact that |
| 144 | -fossil supports disconnected operation.</p> | |
| 144 | +fossil supports disconnected operation. | |
| 145 | 145 | |
| 146 | -<p>As for bloat: Fossil is a single self-contained executable. | |
| 146 | +As for bloat: Fossil is a single self-contained executable. | |
| 147 | 147 | You do not need any other packages |
| 148 | 148 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 149 | 149 | sqlite, and so forth) |
| 150 | 150 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 151 | 151 | by itself. And the self-contained fossil |
| 152 | 152 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 153 | 153 | grown in the years since the previous sentence was written but is still |
| 154 | 154 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 155 | -Fossil is the very opposite of bloat.</p> | |
| 155 | +Fossil is the very opposite of bloat. | |
| 156 | 156 | </blockquote> |
| 157 | 157 | |
| 158 | 158 | |
| 159 | 159 | </nowiki> |
| 160 | 160 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -1,25 +1,25 @@ | |
| 1 | <title>Questions And Criticisms</title> |
| 2 | <nowiki> |
| 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | |
| 5 | <p>This page is a collection of real questions and criticisms that were |
| 6 | raised against Fossil early in its history (circa 2008). |
| 7 | This page is old and has not been kept up-to-date. See the |
| 8 | </nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p> |
| 9 | |
| 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | |
| 13 | <blockquote> |
| 14 | <p>I wrote fossil because none of the |
| 15 | other available DVCSes met my needs. If the other DVCSes do |
| 16 | meet your needs, then you might not need fossil. But they |
| 17 | don't meet mine, and so fossil is necessary for me.</p> |
| 18 | |
| 19 | <p>Features provided by fossil that one does not get with other |
| 20 | DVCSes include:</p> |
| 21 | |
| 22 | <ol> |
| 23 | <li> Integrated <a href="wikitheory.wiki">wiki</a>. </li> |
| 24 | <li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li> |
| 25 | <li> Immutable artifacts </li> |
| @@ -67,30 +67,30 @@ | |
| 67 | |
| 68 | <b>Fossil looks like the bug tracker that would be in your |
| 69 | Linksys Router's administration screen.</b> |
| 70 | |
| 71 | <blockquote> |
| 72 | <p>I take a pragmatic approach to software: form follows function. |
| 73 | To me, it is more important to have a reliable, fast, efficient, |
| 74 | enduring, and simple DVCS than one that looks pretty.</p> |
| 75 | |
| 76 | <p>On the other hand, if you have patches that improve the appearance |
| 77 | of Fossil without seriously compromising its reliability, performance, |
| 78 | and/or maintainability, I will be happy to accept them. Fossil is |
| 79 | self-hosting. Send email to request a password that will let |
| 80 | you push to the main fossil repository.</p> |
| 81 | </blockquote> |
| 82 | |
| 83 | <b>It would be useful to have a separate application that |
| 84 | keeps the bug-tracking database in a versioned file. That file can |
| 85 | then be pushed and pulled along with the rest repository.</b> |
| 86 | |
| 87 | <blockquote> |
| 88 | <p>Fossil already <u>does</u> push and pull bugs along with the files |
| 89 | in your repository. |
| 90 | But fossil does <u>not</u> track bugs as files in the source tree. |
| 91 | That approach to bug tracking was rejected for three reasons:</p> |
| 92 | |
| 93 | <ol> |
| 94 | <li> Check-ins in fossil are immutable. So if |
| 95 | tickets were part of the check-in, then there would be no way to add |
| 96 | new tickets to a check-in as new bugs are discovered. |
| @@ -105,12 +105,12 @@ | |
| 105 | of tickets to developers with check-in privileges and an installed |
| 106 | copy of the fossil executable. Casual passers-by on the internet should |
| 107 | be permitted to create tickets. |
| 108 | </ol> |
| 109 | |
| 110 | <p>These points are reiterated in the opening paragraphs of |
| 111 | the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.</p> |
| 112 | </blockquote> |
| 113 | |
| 114 | <b>Fossil is already the name of a plan9 versioned |
| 115 | append-only filesystem.</b> |
| 116 | |
| @@ -136,24 +136,24 @@ | |
| 136 | directly in the VCS - either they are under-featured compared to full |
| 137 | software like Trac, or the VCS is massively bloated compared to |
| 138 | Subversion or Bazaar.</b> |
| 139 | |
| 140 | <blockquote> |
| 141 | <p>I have no doubt that Trac has many features that fossil lacks. But that |
| 142 | is not the point. Fossil has several key features that Trac lacks and that |
| 143 | I need: most notably the fact that |
| 144 | fossil supports disconnected operation.</p> |
| 145 | |
| 146 | <p>As for bloat: Fossil is a single self-contained executable. |
| 147 | You do not need any other packages |
| 148 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 149 | sqlite, and so forth) |
| 150 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 151 | by itself. And the self-contained fossil |
| 152 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 153 | grown in the years since the previous sentence was written but is still |
| 154 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 155 | Fossil is the very opposite of bloat.</p> |
| 156 | </blockquote> |
| 157 | |
| 158 | |
| 159 | </nowiki> |
| 160 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -1,25 +1,25 @@ | |
| 1 | <title>Questions And Criticisms</title> |
| 2 | <nowiki> |
| 3 | <h1 align="center">Questions And Criticisms</h1> |
| 4 | |
| 5 | This page is a collection of real questions and criticisms that were |
| 6 | raised against Fossil early in its history (circa 2008). |
| 7 | This page is old and has not been kept up-to-date. See the |
| 8 | </nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>. |
| 9 | |
| 10 | <b>Fossil sounds like a lot of reinvention of the wheel. |
| 11 | Why create your own DVCS when you could have reused mercurial?</b> |
| 12 | |
| 13 | <blockquote> |
| 14 | I wrote fossil because none of the |
| 15 | other available DVCSes met my needs. If the other DVCSes do |
| 16 | meet your needs, then you might not need fossil. But they |
| 17 | don't meet mine, and so fossil is necessary for me. |
| 18 | |
| 19 | Features provided by fossil that one does not get with other |
| 20 | DVCSes include: |
| 21 | |
| 22 | <ol> |
| 23 | <li> Integrated <a href="wikitheory.wiki">wiki</a>. </li> |
| 24 | <li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li> |
| 25 | <li> Immutable artifacts </li> |
| @@ -67,30 +67,30 @@ | |
| 67 | |
| 68 | <b>Fossil looks like the bug tracker that would be in your |
| 69 | Linksys Router's administration screen.</b> |
| 70 | |
| 71 | <blockquote> |
| 72 | I take a pragmatic approach to software: form follows function. |
| 73 | To me, it is more important to have a reliable, fast, efficient, |
| 74 | enduring, and simple DVCS than one that looks pretty. |
| 75 | |
| 76 | On the other hand, if you have patches that improve the appearance |
| 77 | of Fossil without seriously compromising its reliability, performance, |
| 78 | and/or maintainability, I will be happy to accept them. Fossil is |
| 79 | self-hosting. Send email to request a password that will let |
| 80 | you push to the main fossil repository. |
| 81 | </blockquote> |
| 82 | |
| 83 | <b>It would be useful to have a separate application that |
| 84 | keeps the bug-tracking database in a versioned file. That file can |
| 85 | then be pushed and pulled along with the rest repository.</b> |
| 86 | |
| 87 | <blockquote> |
| 88 | Fossil already <u>does</u> push and pull bugs along with the files |
| 89 | in your repository. |
| 90 | But fossil does <u>not</u> track bugs as files in the source tree. |
| 91 | That approach to bug tracking was rejected for three reasons: |
| 92 | |
| 93 | <ol> |
| 94 | <li> Check-ins in fossil are immutable. So if |
| 95 | tickets were part of the check-in, then there would be no way to add |
| 96 | new tickets to a check-in as new bugs are discovered. |
| @@ -105,12 +105,12 @@ | |
| 105 | of tickets to developers with check-in privileges and an installed |
| 106 | copy of the fossil executable. Casual passers-by on the internet should |
| 107 | be permitted to create tickets. |
| 108 | </ol> |
| 109 | |
| 110 | These points are reiterated in the opening paragraphs of |
| 111 | the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document. |
| 112 | </blockquote> |
| 113 | |
| 114 | <b>Fossil is already the name of a plan9 versioned |
| 115 | append-only filesystem.</b> |
| 116 | |
| @@ -136,24 +136,24 @@ | |
| 136 | directly in the VCS - either they are under-featured compared to full |
| 137 | software like Trac, or the VCS is massively bloated compared to |
| 138 | Subversion or Bazaar.</b> |
| 139 | |
| 140 | <blockquote> |
| 141 | I have no doubt that Trac has many features that fossil lacks. But that |
| 142 | is not the point. Fossil has several key features that Trac lacks and that |
| 143 | I need: most notably the fact that |
| 144 | fossil supports disconnected operation. |
| 145 | |
| 146 | As for bloat: Fossil is a single self-contained executable. |
| 147 | You do not need any other packages |
| 148 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 149 | sqlite, and so forth) |
| 150 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 151 | by itself. And the self-contained fossil |
| 152 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 153 | grown in the years since the previous sentence was written but is still |
| 154 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 155 | Fossil is the very opposite of bloat. |
| 156 | </blockquote> |
| 157 | |
| 158 | |
| 159 | </nowiki> |
| 160 |
+122
-143
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -1,20 +1,21 @@ | ||
| 1 | 1 | <title>Fossil Quick Start Guide</title> |
| 2 | 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | 3 | |
| 4 | -<p>This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly | |
| 5 | -and painlessly.</p> | |
| 4 | +This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly | |
| 5 | +and painlessly. | |
| 6 | 6 | |
| 7 | 7 | <h2 id="install">Installing</h2> |
| 8 | 8 | |
| 9 | -<p>Fossil is a single self-contained C program. You need to | |
| 9 | +Fossil is a single self-contained C program. You need to | |
| 10 | 10 | either download a |
| 11 | 11 | [https://fossil-scm.org/home/uv/download.html|precompiled |
| 12 | 12 | binary] |
| 13 | 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | 14 | Install Fossil by putting the fossil binary |
| 15 | -someplace on your $PATH.</p> | |
| 15 | +someplace on your $PATH. | |
| 16 | + | |
| 16 | 17 | You can test that Fossil is present and working like this: |
| 17 | 18 | |
| 18 | 19 | <blockquote> |
| 19 | 20 | <b> |
| 20 | 21 | fossil version<br> |
| @@ -22,15 +23,15 @@ | ||
| 22 | 23 | </b> |
| 23 | 24 | </blockquote> |
| 24 | 25 | |
| 25 | 26 | <h2 id="workflow" name="fslclone">General Work Flow</h2> |
| 26 | 27 | |
| 27 | -<p>Fossil works with repository files (a database in a single file with the project's | |
| 28 | +Fossil works with repository files (a database in a single file with the project's | |
| 28 | 29 | complete history) and with checked-out local trees (the working directory |
| 29 | 30 | you use to do your work). |
| 30 | 31 | (See [./glossary.md | the glossary] for more background.) |
| 31 | -The workflow looks like this:</p> | |
| 32 | +The workflow looks like this: | |
| 32 | 33 | |
| 33 | 34 | <ul> |
| 34 | 35 | <li>Create or clone a repository file. ([/help/init|fossil init] or |
| 35 | 36 | [/help/clone | fossil clone]) |
| 36 | 37 | <li>Check out a local tree. ([/help/open | fossil open]) |
| @@ -39,17 +40,17 @@ | ||
| 39 | 40 | </ul> |
| 40 | 41 | |
| 41 | 42 | Fossil can be entirely driven from the command line. Many features |
| 42 | 43 | can also be conveniently accessed from the built-in web user interface. |
| 43 | 44 | |
| 44 | -<p>The following sections give a brief overview of these | |
| 45 | -operations.</p> | |
| 45 | +The following sections give a brief overview of these | |
| 46 | +operations. | |
| 46 | 47 | |
| 47 | 48 | <h2 id="new">Starting A New Project</h2> |
| 48 | 49 | |
| 49 | -<p>To start a new project with fossil create a new empty repository | |
| 50 | -this way: ([/help/init | more info]) </p> | |
| 50 | +To start a new project with fossil create a new empty repository | |
| 51 | +this way: ([/help/init | more info]) | |
| 51 | 52 | |
| 52 | 53 | <blockquote> |
| 53 | 54 | <b>fossil init </b><i> repository-filename</i> |
| 54 | 55 | </blockquote> |
| 55 | 56 | |
| @@ -57,23 +58,23 @@ | ||
| 57 | 58 | The <tt>.fossil</tt> extension is traditional but only required if you are going to use the |
| 58 | 59 | <tt>[/help/server | fossil server DIRECTORY]</tt> feature.” |
| 59 | 60 | |
| 60 | 61 | <h2 id="clone">Cloning An Existing Repository</h2> |
| 61 | 62 | |
| 62 | -<p>Most fossil operations interact with a repository that is on the | |
| 63 | +Most fossil operations interact with a repository that is on the | |
| 63 | 64 | local disk drive, not on a remote system. Hence, before accessing |
| 64 | 65 | a remote repository it is necessary to make a local copy of that |
| 65 | 66 | repository. Making a local copy of a remote repository is called |
| 66 | -"cloning".</p> | |
| 67 | +"cloning". | |
| 67 | 68 | |
| 68 | -<p>Clone a remote repository as follows: ([/help/clone | more info])</p> | |
| 69 | +Clone a remote repository as follows: ([/help/clone | more info]) | |
| 69 | 70 | |
| 70 | 71 | <blockquote> |
| 71 | 72 | <b>fossil clone</b> <i>URL repository-filename</i> |
| 72 | 73 | </blockquote> |
| 73 | 74 | |
| 74 | -<p>The <i>URL</i> specifies the fossil repository | |
| 75 | +The <i>URL</i> specifies the fossil repository | |
| 75 | 76 | you want to clone. The <i>repository-filename</i> is the new local |
| 76 | 77 | filename into which the cloned repository will be written. For |
| 77 | 78 | example, to clone the source code of Fossil itself: |
| 78 | 79 | |
| 79 | 80 | <blockquote> |
| @@ -94,25 +95,25 @@ | ||
| 94 | 95 | server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br> |
| 95 | 96 | admin-user: exampleuser (password is "yoWgDR42iv")><br> |
| 96 | 97 | </tt></b> |
| 97 | 98 | </blockquote> |
| 98 | 99 | |
| 99 | -<p>If the remote repository requires a login, include a | |
| 100 | +If the remote repository requires a login, include a | |
| 100 | 101 | userid in the URL like this: |
| 101 | 102 | |
| 102 | 103 | <blockquote> |
| 103 | 104 | <b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b> |
| 104 | 105 | </blockquote> |
| 105 | 106 | |
| 106 | -<p>You will be prompted separately for the password. | |
| 107 | - Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. | |
| 108 | - For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") </p> | |
| 109 | - | |
| 110 | -<p>If you are behind a restrictive firewall, you might need | |
| 111 | -to <a href="#proxy">specify an HTTP proxy</a>.</p> | |
| 112 | - | |
| 113 | -<p>A Fossil repository is a single disk file. Instead of cloning, | |
| 107 | +You will be prompted separately for the password. | |
| 108 | +Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. | |
| 109 | +For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") | |
| 110 | + | |
| 111 | +If you are behind a restrictive firewall, you might need | |
| 112 | +to <a href="#proxy">specify an HTTP proxy</a>. | |
| 113 | + | |
| 114 | +A Fossil repository is a single disk file. Instead of cloning, | |
| 114 | 115 | you can just make a copy of the repository file (for example, using |
| 115 | 116 | "scp"). Note, however, that the repository file contains auxiliary |
| 116 | 117 | information above and beyond the versioned files, including some |
| 117 | 118 | sensitive information such as password hashes and email addresses. If you |
| 118 | 119 | want to share Fossil repositories directly by copying, consider running the |
| @@ -119,11 +120,11 @@ | ||
| 119 | 120 | [/help/scrub|fossil scrub] command to remove sensitive information |
| 120 | 121 | before transmitting the file. |
| 121 | 122 | |
| 122 | 123 | <h2 id="import">Importing From Another Version Control System</h2> |
| 123 | 124 | |
| 124 | -<p>Rather than start a new project, or clone an existing Fossil project, | |
| 125 | +Rather than start a new project, or clone an existing Fossil project, | |
| 125 | 126 | you might prefer to |
| 126 | 127 | <a href="./inout.wiki">import an existing Git project</a> |
| 127 | 128 | into Fossil using the [/help/import | fossil import] command. |
| 128 | 129 | |
| 129 | 130 | You can even decide to export your project back into git using the |
| @@ -137,14 +138,14 @@ | ||
| 137 | 138 | [https://www.mercurial-scm.org/|Mercurial]. |
| 138 | 139 | Fossil can also import [https://subversion.apache.org/|Subversion projects] directly. |
| 139 | 140 | |
| 140 | 141 | <h2 id="checkout">Checking Out A Local Tree</h2> |
| 141 | 142 | |
| 142 | -<p>To work on a project in fossil, you need to check out a local | |
| 143 | +To work on a project in fossil, you need to check out a local | |
| 143 | 144 | copy of the source tree. Create the directory you want to be |
| 144 | 145 | the root of your tree and cd into that directory. Then |
| 145 | -do this: ([/help/open | more info])</p> | |
| 146 | +do this: ([/help/open | more info]) | |
| 146 | 147 | |
| 147 | 148 | <blockquote> |
| 148 | 149 | <b>fossil open </b><i> repository-filename</i> |
| 149 | 150 | </blockquote> |
| 150 | 151 | |
| @@ -160,15 +161,15 @@ | ||
| 160 | 161 | </tt></b> |
| 161 | 162 | </blockquote> |
| 162 | 163 | |
| 163 | 164 | (or "fossil open ..\myclone.fossil" on Windows). |
| 164 | 165 | |
| 165 | -<p>This leaves you with the newest version of the tree | |
| 166 | +This leaves you with the newest version of the tree | |
| 166 | 167 | checked out. |
| 167 | 168 | From anywhere underneath the root of your local tree, you |
| 168 | 169 | can type commands like the following to find out the status of |
| 169 | -your local tree:</p> | |
| 170 | +your local tree: | |
| 170 | 171 | |
| 171 | 172 | <blockquote> |
| 172 | 173 | <b>[/help/info | fossil info]</b><br> |
| 173 | 174 | <b>[/help/status | fossil status]</b><br> |
| 174 | 175 | <b>[/help/changes | fossil changes]</b><br> |
| @@ -176,60 +177,63 @@ | ||
| 176 | 177 | <b>[/help/timeline | fossil timeline]</b><br> |
| 177 | 178 | <b>[/help/ls | fossil ls]</b><br> |
| 178 | 179 | <b>[/help/branch | fossil branch]</b><br> |
| 179 | 180 | </blockquote> |
| 180 | 181 | |
| 181 | -<p>If you created a new repository using "fossil init" some commands will not | |
| 182 | -produce much output.</p> | |
| 182 | +If you created a new repository using "fossil init" some commands will not | |
| 183 | +produce much output. | |
| 183 | 184 | |
| 184 | -<p>Note that Fossil allows you to make multiple check-outs in | |
| 185 | +Note that Fossil allows you to make multiple check-outs in | |
| 185 | 186 | separate directories from the same repository. This enables you, |
| 186 | 187 | for example, to do builds from multiple branches or versions at |
| 187 | -the same time without having to generate extra clones.</p> | |
| 188 | +the same time without having to generate extra clones. | |
| 188 | 189 | |
| 189 | -<p>To switch a checkout between different versions and branches, | |
| 190 | -use:</p> | |
| 190 | +To switch a checkout between different versions and branches, | |
| 191 | +use:< | |
| 191 | 192 | |
| 192 | 193 | <blockquote> |
| 193 | 194 | <b>[/help/update | fossil update]</b><br> |
| 194 | 195 | <b>[/help/checkout | fossil checkout]</b><br> |
| 195 | 196 | </blockquote> |
| 196 | 197 | |
| 197 | -<p>[/help/update | update] honors the "autosync" option and | |
| 198 | +[/help/update | update] honors the "autosync" option and | |
| 198 | 199 | does a "soft" switch, merging any local changes into the target |
| 199 | 200 | version, whereas [/help/checkout | checkout] does not |
| 200 | 201 | automatically sync and does a "hard" switch, overwriting local |
| 201 | -changes if told to do so.</p> | |
| 202 | +changes if told to do so. | |
| 202 | 203 | |
| 203 | 204 | <h2 id="changes">Making and Committing Changes</h2> |
| 204 | 205 | |
| 205 | -<p>To add new files to your project or remove existing ones, use these | |
| 206 | -commands:</p> | |
| 206 | +To add new files to your project or remove existing ones, use these | |
| 207 | +commands: | |
| 207 | 208 | |
| 208 | 209 | <blockquote> |
| 209 | 210 | <b>[/help/add | fossil add]</b> <i>file...</i><br> |
| 210 | 211 | <b>[/help/rm | fossil rm]</b> <i>file...</i><br> |
| 211 | 212 | <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br> |
| 212 | 213 | </blockquote> |
| 213 | 214 | |
| 214 | -<p>The command:</p> | |
| 215 | +The command: | |
| 216 | + | |
| 215 | 217 | <blockquote> |
| 216 | 218 | <b> |
| 217 | 219 | [/help/changes | fossil changes]</b> |
| 218 | 220 | </blockquote> |
| 219 | -<p>lists files that have changed since the last commit to the repository. For | |
| 220 | -example, if you edit the file "README.md":</p> | |
| 221 | + | |
| 222 | +lists files that have changed since the last commit to the repository. For | |
| 223 | +example, if you edit the file "README.md": | |
| 221 | 224 | |
| 222 | 225 | <blockquote> |
| 223 | 226 | <b> |
| 224 | 227 | fossil changes<br> |
| 225 | 228 | EDITED README.md |
| 226 | 229 | </b> |
| 227 | 230 | </blockquote> |
| 228 | 231 | |
| 229 | -<p>To see exactly what change was made you can use the command</p> | |
| 230 | -[/help/diff | fossil diff]: | |
| 232 | +To see exactly what change was made you can use the command | |
| 233 | +<b>[/help/diff | fossil diff]</b>: | |
| 234 | + | |
| 231 | 235 | <blockquote> |
| 232 | 236 | <b> |
| 233 | 237 | fossil diff <br><tt> |
| 234 | 238 | Index: README.md<br> |
| 235 | 239 | ============================================================<br> |
| @@ -239,17 +243,19 @@ | ||
| 239 | 243 | +Made some changes to the project<br> |
| 240 | 244 | # Original text<br> |
| 241 | 245 | </tt></b> |
| 242 | 246 | </blockquote> |
| 243 | 247 | |
| 244 | -<p>"fossil diff" is the difference between your tree on disk now and as the tree was | |
| 245 | -when you did "fossil open". An open is the first checkout from a repository | |
| 246 | -into a new directory. </p> | |
| 248 | +"fossil diff" shows the difference between your tree on disk now and as | |
| 249 | +the tree was when you last committed changes. If you haven't committed | |
| 250 | +yet, then it shows the difference relative to the tip-of-trunk commit in | |
| 251 | +the repository, being what you get when you "fossil open" a repository | |
| 252 | +without specifying a version, populating the working directory. | |
| 247 | 253 | |
| 248 | -<p>To see the most recent changes made to the repository by other users, use "fossil timeline" to | |
| 254 | +To see the most recent changes made to the repository by other users, use "fossil timeline" to | |
| 249 | 255 | find out the most recent commit, and then "fossil diff" between that commit and the |
| 250 | -current tree: </p> | |
| 256 | +current tree: | |
| 251 | 257 | <blockquote> |
| 252 | 258 | <b> |
| 253 | 259 | fossil timeline <br><tt> |
| 254 | 260 | === 2021-03-28 === <br> |
| 255 | 261 | 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br> |
| @@ -269,11 +275,11 @@ | ||
| 269 | 275 | </blockquote> |
| 270 | 276 | |
| 271 | 277 | "current" is an alias for the checkout version, so the command |
| 272 | 278 | "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. |
| 273 | 279 | |
| 274 | -<p>To commit your changes to a local-only repository:</p> | |
| 280 | +To commit your changes to a local-only repository: | |
| 275 | 281 | <blockquote> |
| 276 | 282 | <b> |
| 277 | 283 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 278 | 284 | # Enter a commit message for this check-in. Lines beginning with # are ignored.<br> |
| 279 | 285 | #<br> |
| @@ -284,163 +290,136 @@ | ||
| 284 | 290 | Edited file to add description of code changes<br> |
| 285 | 291 | New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br> |
| 286 | 292 | </tt></b> |
| 287 | 293 | </blockquote> |
| 288 | 294 | |
| 289 | -<p>You will be prompted for check-in comments using whatever editor | |
| 295 | +You will be prompted for check-in comments using whatever editor | |
| 290 | 296 | is specified by your VISUAL or EDITOR environment variable. If none is |
| 291 | -specified Fossil uses line-editing in the terminal.</p> | |
| 297 | +specified Fossil uses line-editing in the terminal. | |
| 292 | 298 | |
| 293 | -<p>To commit your changes to a repository that was cloned from remote you | |
| 294 | -perform the same actions but the results are different. Fossil | |
| 295 | -defaults to 'autosync' mode, a single-stage commit that sends all changes | |
| 296 | -committed to the local repository immediately on to the remote parent repository. This | |
| 297 | -only works if you have write permission to the remote respository.</p> | |
| 299 | +To commit your changes to a repository that was cloned from a remote | |
| 300 | +repository, you give the same command, but the results are different. | |
| 301 | +Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a | |
| 302 | +single-stage commit that sends all changes committed to the local | |
| 303 | +repository immediately on to the remote parent repository. This only | |
| 304 | +works if you have write permission to the remote respository. | |
| 298 | 305 | |
| 299 | 306 | <h2 id="naming">Naming of Files, Checkins, and Branches</h2> |
| 300 | 307 | |
| 301 | -<p>Fossil deals with information artifacts. This Quickstart document only deals | |
| 308 | +Fossil deals with information artifacts. This Quickstart document only deals | |
| 302 | 309 | with files and collections of files, but be aware there are also tickets, wiki pages and more. |
| 303 | 310 | Every artifact in Fossil has a universally-unique hash id, and may also have a |
| 304 | -human-readable name.</p> | |
| 311 | +human-readable name. | |
| 305 | 312 | |
| 306 | -<p>The following are all equivalent ways of identifying a Fossil file, | |
| 307 | -checkin or branch artifact:</p> | |
| 313 | +The following are all equivalent ways of identifying a Fossil file, | |
| 314 | +checkin or branch artifact: | |
| 308 | 315 | |
| 309 | 316 | <ul> |
| 310 | 317 | <li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a |
| 311 | 318 | <li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters |
| 312 | 319 | <li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash |
| 313 | 320 | </ul> |
| 314 | 321 | |
| 315 | -<p>A special convenience branch is "trunk", which is Fossil's default branch name for | |
| 322 | +A special convenience branch is "trunk", which is Fossil's default branch name for | |
| 316 | 323 | the first checkin, and the default for any time a branch name is needed but not |
| 317 | -specified.</p> | |
| 324 | +specified. | |
| 318 | 325 | |
| 319 | 326 | This will get you started on identifying checkins. The |
| 320 | 327 | <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including |
| 321 | 328 | how timestamps can also be used. |
| 322 | 329 | |
| 323 | 330 | <h2 id="config">Configuring Your Local Repository</h2> |
| 324 | 331 | |
| 325 | -<p>When you create a new repository, either by cloning an existing | |
| 332 | +When you create a new repository, either by cloning an existing | |
| 326 | 333 | project or create a new project of your own, you usually want to do some |
| 327 | 334 | local configuration. This is easily accomplished using the web-server |
| 328 | 335 | that is built into fossil. Start the fossil web server like this: |
| 329 | -([/help/ui | more info])</p> | |
| 336 | +([/help/ui | more info]) | |
| 330 | 337 | |
| 331 | 338 | <blockquote> |
| 332 | 339 | <b>fossil ui </b><i> repository-filename</i> |
| 333 | 340 | </blockquote> |
| 334 | 341 | |
| 335 | -<p>You can omit the <i>repository-filename</i> from the command above | |
| 336 | -if you are inside a checked-out local tree.</p> | |
| 342 | +You can omit the <i>repository-filename</i> from the command above | |
| 343 | +if you are inside a checked-out local tree. | |
| 337 | 344 | |
| 338 | -<p>This starts a web server then automatically launches your | |
| 345 | +This starts a web server then automatically launches your | |
| 339 | 346 | web browser and makes it point to this web server. If your system |
| 340 | 347 | has an unusual configuration, fossil might not be able to figure out |
| 341 | 348 | how to start your web browser. In that case, first tell fossil |
| 342 | -where to find your web browser using a command like this:</p> | |
| 349 | +where to find your web browser using a command like this: | |
| 343 | 350 | |
| 344 | 351 | <blockquote> |
| 345 | 352 | <b>fossil setting web-browser </b><i> path-to-web-browser</i> |
| 346 | 353 | </blockquote> |
| 347 | 354 | |
| 348 | -<p>By default, fossil does not require a login for HTTP connections | |
| 355 | +By default, fossil does not require a login for HTTP connections | |
| 349 | 356 | coming in from the IP loopback address 127.0.0.1. You can, and perhaps |
| 350 | -should, change this after you create a few users.</p> | |
| 351 | - | |
| 352 | -<p>When you are finished configuring, just press Control-C or use | |
| 353 | -the <b>kill</b> command to shut down the mini-server.</p> | |
| 354 | - | |
| 355 | -<h2 id="changes">Making Changes</h2> | |
| 356 | - | |
| 357 | -<p>To add new files to your project, or remove old files, use these | |
| 358 | -commands:</p> | |
| 359 | - | |
| 360 | -<blockquote> | |
| 361 | -<b>[/help/add | fossil add]</b> <i>file...</i><br> | |
| 362 | -<b>[/help/rm | fossil rm]</b> <i>file...</i><br> | |
| 363 | -<b>[/help/addremove | fossil addremove]</b> <i>file...</i><br> | |
| 364 | -</blockquote> | |
| 365 | - | |
| 366 | -<p>You can also edit files freely. Once you are ready to commit | |
| 367 | -your changes, type:</p> | |
| 368 | - | |
| 369 | -<blockquote> | |
| 370 | -<b>[/help/commit | fossil commit]</b> | |
| 371 | -</blockquote> | |
| 372 | - | |
| 373 | -<p>You will be prompted for check-in comments using whatever editor | |
| 374 | -is specified by your VISUAL or EDITOR environment variable.</p> | |
| 375 | - | |
| 376 | -In the default configuration, the [/help/commit|commit] | |
| 377 | -command will also automatically [/help/push|push] your changes, but that | |
| 378 | -feature can be disabled. (More information about | |
| 379 | -[./concepts.wiki#workflow|autosync] and how to disable it.) | |
| 380 | -Remember that your coworkers can not see your changes until you | |
| 381 | -commit and push them.</p> | |
| 357 | +should, change this after you create a few users. | |
| 358 | + | |
| 359 | +When you are finished configuring, just press Control-C or use | |
| 360 | +the <b>kill</b> command to shut down the mini-server. | |
| 382 | 361 | |
| 383 | 362 | <h2 id="sharing">Sharing Changes</h2> |
| 384 | 363 | |
| 385 | -<p>When [./concepts.wiki#workflow|autosync] is turned off, | |
| 364 | +When [./concepts.wiki#workflow|autosync] is turned off, | |
| 386 | 365 | the changes you [/help/commit | commit] are only |
| 387 | 366 | on your local repository. |
| 388 | -To share those changes with other repositories, do:</p> | |
| 367 | +To share those changes with other repositories, do: | |
| 389 | 368 | |
| 390 | 369 | <blockquote> |
| 391 | 370 | <b>[/help/push | fossil push]</b> <i>URL</i> |
| 392 | 371 | </blockquote> |
| 393 | 372 | |
| 394 | -<p>Where <i>URL</i> is the http: URL of the server repository you | |
| 373 | +Where <i>URL</i> is the http: URL of the server repository you | |
| 395 | 374 | want to share your changes with. If you omit the <i>URL</i> argument, |
| 396 | -fossil will use whatever server you most recently synced with.</p> | |
| 375 | +fossil will use whatever server you most recently synced with. | |
| 397 | 376 | |
| 398 | -<p>The [/help/push | push] command only sends your changes to others. To | |
| 377 | +The [/help/push | push] command only sends your changes to others. To | |
| 399 | 378 | Receive changes from others, use [/help/pull | pull]. Or go both ways at |
| 400 | -once using [/help/sync | sync]:</p> | |
| 379 | +once using [/help/sync | sync]: | |
| 401 | 380 | |
| 402 | 381 | <blockquote> |
| 403 | 382 | <b>[/help/pull | fossil pull]</b> <i>URL</i><br> |
| 404 | 383 | <b>[/help/sync | fossil sync]</b> <i>URL</i> |
| 405 | 384 | </blockquote> |
| 406 | 385 | |
| 407 | -<p>When you pull in changes from others, they go into your repository, | |
| 386 | +When you pull in changes from others, they go into your repository, | |
| 408 | 387 | not into your checked-out local tree. To get the changes into your |
| 409 | -local tree, use [/help/update | update]:</p> | |
| 388 | +local tree, use [/help/update | update]: | |
| 410 | 389 | |
| 411 | 390 | <blockquote> |
| 412 | 391 | <b>[/help/update | fossil update]</b> <i>VERSION</i> |
| 413 | 392 | </blockquote> |
| 414 | 393 | |
| 415 | -<p>The <i>VERSION</i> can be the name of a branch or tag or any | |
| 394 | +The <i>VERSION</i> can be the name of a branch or tag or any | |
| 416 | 395 | abbreviation to the 40-character |
| 417 | 396 | artifact identifier for a particular check-in, or it can be a |
| 418 | 397 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 419 | 398 | If you omit |
| 420 | 399 | the <i>VERSION</i>, then fossil moves you to the |
| 421 | -latest version of the branch your are currently on.</p> | |
| 400 | +latest version of the branch your are currently on. | |
| 422 | 401 | |
| 423 | -<p>The default behavior is for [./concepts.wiki#workflow|autosync] to | |
| 402 | +The default behavior is for [./concepts.wiki#workflow|autosync] to | |
| 424 | 403 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 425 | 404 | when you run [/help/update|update] and a [/help/push|push] happens |
| 426 | 405 | automatically after you [/help/commit|commit]. So in normal practice, |
| 427 | 406 | the push, pull, and sync commands are rarely used. But it is important |
| 428 | -to know about them, all the same.</p> | |
| 407 | +to know about them, all the same. | |
| 429 | 408 | |
| 430 | 409 | <blockquote> |
| 431 | 410 | <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> |
| 432 | 411 | </blockquote> |
| 433 | 412 | |
| 434 | -<p>Is similar to update except that it does not honor the autosync | |
| 413 | +Is similar to update except that it does not honor the autosync | |
| 435 | 414 | setting, nor does it merge in local changes - it prefers to overwrite |
| 436 | 415 | them and fails if local changes exist unless the <tt>--force</tt> |
| 437 | -flag is used.</p> | |
| 416 | +flag is used. | |
| 438 | 417 | |
| 439 | 418 | <h2 id="branch" name="merge">Branching And Merging</h2> |
| 440 | 419 | |
| 441 | -<p>Use the --branch option to the [/help/commit | commit] command | |
| 420 | +Use the --branch option to the [/help/commit | commit] command | |
| 442 | 421 | to start a new branch. Note that in Fossil, branches are normally |
| 443 | 422 | created when you commit, not before you start editing. You can |
| 444 | 423 | use the [/help/branch | branch new] command to create a new branch |
| 445 | 424 | before you start editing, if you want, but most people just wait |
| 446 | 425 | until they are ready to commit. |
| @@ -447,25 +426,25 @@ | ||
| 447 | 426 | |
| 448 | 427 | To merge two branches back together, first |
| 449 | 428 | [/help/update | update] to the branch you want to merge into. |
| 450 | 429 | Then do a [/help/merge|merge] of the other branch that you want to incorporate |
| 451 | 430 | the changes from. For example, to merge "featureX" changes into "trunk" |
| 452 | -do this:</p> | |
| 431 | +do this: | |
| 453 | 432 | |
| 454 | 433 | <blockquote> |
| 455 | 434 | <b>fossil [/help/update|update] trunk</b><br> |
| 456 | 435 | <b>fossil [/help/merge|merge] featureX</b><br> |
| 457 | 436 | <i># make sure the merge didn't break anything...</i><br> |
| 458 | 437 | <b>fossil [/help/commit|commit] |
| 459 | 438 | </blockquote> |
| 460 | 439 | |
| 461 | -<p>The argument to the [/help/merge|merge] command can be any of the | |
| 440 | +The argument to the [/help/merge|merge] command can be any of the | |
| 462 | 441 | version identifier forms that work for [/help/update|update]. |
| 463 | 442 | ([./checkin_names.wiki|more info].) |
| 464 | 443 | The merge command has options to cherry-pick individual |
| 465 | 444 | changes, or to back out individual changes, if you don't want to |
| 466 | -do a full merge.</p> | |
| 445 | +do a full merge. | |
| 467 | 446 | |
| 468 | 447 | The merge command puts all changes in your working check-out. |
| 469 | 448 | No changes are made to the repository. |
| 470 | 449 | You must run [/help/commit|commit] separately |
| 471 | 450 | to add the merge changes into your repository to make them persistent |
| @@ -478,110 +457,110 @@ | ||
| 478 | 457 | multiple merges. So even if you have merged the featureX branch |
| 479 | 458 | into trunk previously, you can do so again and Fossil will automatically |
| 480 | 459 | know to pull in only those changes that have occurred since the previous |
| 481 | 460 | merge. |
| 482 | 461 | |
| 483 | -<p>If a merge or update doesn't work out (perhaps something breaks or | |
| 484 | -there are many merge conflicts) then you back up using:</p> | |
| 462 | +If a merge or update doesn't work out (perhaps something breaks or | |
| 463 | +there are many merge conflicts) then you back up using: | |
| 485 | 464 | |
| 486 | 465 | <blockquote> |
| 487 | 466 | <b>[/help/undo | fossil undo]</b> |
| 488 | 467 | </blockquote> |
| 489 | 468 | |
| 490 | -<p>This will back out the changes that the merge or update made to the | |
| 469 | +This will back out the changes that the merge or update made to the | |
| 491 | 470 | working checkout. There is also a [/help/redo|redo] command if you undo by |
| 492 | 471 | mistake. Undo and redo only work for changes that have |
| 493 | 472 | not yet been checked in using commit and there is only a single |
| 494 | -level of undo/redo.</p> | |
| 473 | +level of undo/redo. | |
| 495 | 474 | |
| 496 | 475 | |
| 497 | 476 | <h2 id="server">Setting Up A Server</h2> |
| 498 | 477 | |
| 499 | -<p>Fossil can act as a stand-alone web server using one of these | |
| 500 | -commands:</p> | |
| 478 | +Fossil can act as a stand-alone web server using one of these | |
| 479 | +commands: | |
| 501 | 480 | |
| 502 | 481 | <blockquote> |
| 503 | 482 | <b>[/help/server | fossil server]</b> <i>repository-filename</i><br> |
| 504 | 483 | <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> |
| 505 | 484 | </blockquote> |
| 506 | 485 | |
| 507 | -<p>The <i>repository-filename</i> can be omitted when these commands | |
| 486 | +The <i>repository-filename</i> can be omitted when these commands | |
| 508 | 487 | are run from within an open check-out, which is a particularly useful |
| 509 | 488 | shortcut with the <b>fossil ui</b> command. |
| 510 | 489 | |
| 511 | -<p>The <b>ui</b> command is intended for accessing the web user interface | |
| 490 | +The <b>ui</b> command is intended for accessing the web user interface | |
| 512 | 491 | from a local desktop. (We sometimes call this mode "Fossil UI.") |
| 513 | 492 | The <b>ui</b> command differs from the |
| 514 | 493 | <b>server</b> command by binding to the loopback IP |
| 515 | 494 | address only (thus making the web UI visible only on the |
| 516 | 495 | local machine) and by automatically starting your default web browser, |
| 517 | 496 | pointing it at the running UI |
| 518 | 497 | server. The localhost restriction exists because it also gives anyone |
| 519 | 498 | who can access the resulting web UI full control over the |
| 520 | 499 | repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful |
| 521 | -Setup capabliity].)</p> | |
| 500 | +Setup capabliity].) | |
| 522 | 501 | |
| 523 | -<p>For cross-machine collaboration, use the <b>server</b> command instead, | |
| 502 | +For cross-machine collaboration, use the <b>server</b> command instead, | |
| 524 | 503 | which binds on all IP addresses, does not try to start a web browser, |
| 525 | -and enforces [./caps/ | Fossil's role-based access control system].</p> | |
| 504 | +and enforces [./caps/ | Fossil's role-based access control system]. | |
| 526 | 505 | |
| 527 | -<p>Servers are also easily configured as: | |
| 506 | +Servers are also easily configured as: | |
| 528 | 507 | |
| 529 | 508 | <ul> |
| 530 | 509 | <li>[./server/any/inetd.md|inetd] |
| 531 | 510 | <li>[./server/debian/service.md|systemd] |
| 532 | 511 | <li>[./server/any/cgi.md|CGI] |
| 533 | 512 | <li>[./server/any/scgi.md|SCGI] |
| 534 | 513 | </ul> |
| 535 | 514 | |
| 536 | -<p>…along with [./server/#matrix | several other options].</p> | |
| 515 | +…along with [./server/#matrix | several other options]. | |
| 537 | 516 | |
| 538 | -<p>The [./selfhost.wiki | self-hosting fossil repositories] use | |
| 517 | +The [./selfhost.wiki | self-hosting fossil repositories] use | |
| 539 | 518 | CGI. |
| 540 | 519 | |
| 541 | -<p>You might <i>need</i> to set up a server, whether you know it yet or | |
| 520 | +You might <i>need</i> to set up a server, whether you know it yet or | |
| 542 | 521 | not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server] |
| 543 | -article for details.</p> | |
| 522 | +article for details. | |
| 544 | 523 | |
| 545 | 524 | <h2 id="proxy">HTTP Proxies</h2> |
| 546 | 525 | |
| 547 | -<p>If you are behind a restrictive firewall that requires you to use | |
| 526 | +If you are behind a restrictive firewall that requires you to use | |
| 548 | 527 | an HTTP proxy to reach the internet, then you can configure the proxy |
| 549 | 528 | in three different ways. You can tell fossil about your proxy using |
| 550 | 529 | a command-line option on commands that use the network, |
| 551 | -<b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.</p> | |
| 530 | +<b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>. | |
| 552 | 531 | |
| 553 | 532 | <blockquote> |
| 554 | 533 | <b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i> |
| 555 | 534 | </blockquote> |
| 556 | 535 | |
| 557 | -<p>It is annoying to have to type in the proxy URL every time you | |
| 536 | +It is annoying to have to type in the proxy URL every time you | |
| 558 | 537 | sync your project, though, so you can make the proxy configuration |
| 559 | -persistent using the [/help/setting | setting] command:</p> | |
| 538 | +persistent using the [/help/setting | setting] command: | |
| 560 | 539 | |
| 561 | 540 | <blockquote> |
| 562 | 541 | <b>fossil setting proxy </b><i>Proxy-URL</i> |
| 563 | 542 | </blockquote> |
| 564 | 543 | |
| 565 | -<p>Or, you can set the "<b>http_proxy</b>" environment variable:</p> | |
| 544 | +Or, you can set the "<b>http_proxy</b>" environment variable: | |
| 566 | 545 | |
| 567 | 546 | <blockquote> |
| 568 | 547 | <b>export http_proxy=</b><i>Proxy-URL</i> |
| 569 | 548 | </blockquote> |
| 570 | 549 | |
| 571 | -<p>To stop using the proxy, do:</p> | |
| 550 | +To stop using the proxy, do: | |
| 572 | 551 | |
| 573 | 552 | <blockquote> |
| 574 | 553 | <b>fossil setting proxy off</b> |
| 575 | 554 | </blockquote> |
| 576 | 555 | |
| 577 | -<p>Or unset the environment variable. The fossil setting for the | |
| 556 | +Or unset the environment variable. The fossil setting for the | |
| 578 | 557 | HTTP proxy takes precedence over the environment variable and the |
| 579 | 558 | command-line option overrides both. If you have a persistent |
| 580 | 559 | proxy setting that you want to override for a one-time sync, that |
| 581 | 560 | is easily done on the command-line. For example, to sync with |
| 582 | -a co-worker's repository on your LAN, you might type:</p> | |
| 561 | +a co-worker's repository on your LAN, you might type: | |
| 583 | 562 | |
| 584 | 563 | <blockquote> |
| 585 | 564 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 586 | 565 | </blockquote> |
| 587 | 566 | |
| 588 | 567 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -1,20 +1,21 @@ | |
| 1 | <title>Fossil Quick Start Guide</title> |
| 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | |
| 4 | <p>This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly |
| 5 | and painlessly.</p> |
| 6 | |
| 7 | <h2 id="install">Installing</h2> |
| 8 | |
| 9 | <p>Fossil is a single self-contained C program. You need to |
| 10 | either download a |
| 11 | [https://fossil-scm.org/home/uv/download.html|precompiled |
| 12 | binary] |
| 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | Install Fossil by putting the fossil binary |
| 15 | someplace on your $PATH.</p> |
| 16 | You can test that Fossil is present and working like this: |
| 17 | |
| 18 | <blockquote> |
| 19 | <b> |
| 20 | fossil version<br> |
| @@ -22,15 +23,15 @@ | |
| 22 | </b> |
| 23 | </blockquote> |
| 24 | |
| 25 | <h2 id="workflow" name="fslclone">General Work Flow</h2> |
| 26 | |
| 27 | <p>Fossil works with repository files (a database in a single file with the project's |
| 28 | complete history) and with checked-out local trees (the working directory |
| 29 | you use to do your work). |
| 30 | (See [./glossary.md | the glossary] for more background.) |
| 31 | The workflow looks like this:</p> |
| 32 | |
| 33 | <ul> |
| 34 | <li>Create or clone a repository file. ([/help/init|fossil init] or |
| 35 | [/help/clone | fossil clone]) |
| 36 | <li>Check out a local tree. ([/help/open | fossil open]) |
| @@ -39,17 +40,17 @@ | |
| 39 | </ul> |
| 40 | |
| 41 | Fossil can be entirely driven from the command line. Many features |
| 42 | can also be conveniently accessed from the built-in web user interface. |
| 43 | |
| 44 | <p>The following sections give a brief overview of these |
| 45 | operations.</p> |
| 46 | |
| 47 | <h2 id="new">Starting A New Project</h2> |
| 48 | |
| 49 | <p>To start a new project with fossil create a new empty repository |
| 50 | this way: ([/help/init | more info]) </p> |
| 51 | |
| 52 | <blockquote> |
| 53 | <b>fossil init </b><i> repository-filename</i> |
| 54 | </blockquote> |
| 55 | |
| @@ -57,23 +58,23 @@ | |
| 57 | The <tt>.fossil</tt> extension is traditional but only required if you are going to use the |
| 58 | <tt>[/help/server | fossil server DIRECTORY]</tt> feature.” |
| 59 | |
| 60 | <h2 id="clone">Cloning An Existing Repository</h2> |
| 61 | |
| 62 | <p>Most fossil operations interact with a repository that is on the |
| 63 | local disk drive, not on a remote system. Hence, before accessing |
| 64 | a remote repository it is necessary to make a local copy of that |
| 65 | repository. Making a local copy of a remote repository is called |
| 66 | "cloning".</p> |
| 67 | |
| 68 | <p>Clone a remote repository as follows: ([/help/clone | more info])</p> |
| 69 | |
| 70 | <blockquote> |
| 71 | <b>fossil clone</b> <i>URL repository-filename</i> |
| 72 | </blockquote> |
| 73 | |
| 74 | <p>The <i>URL</i> specifies the fossil repository |
| 75 | you want to clone. The <i>repository-filename</i> is the new local |
| 76 | filename into which the cloned repository will be written. For |
| 77 | example, to clone the source code of Fossil itself: |
| 78 | |
| 79 | <blockquote> |
| @@ -94,25 +95,25 @@ | |
| 94 | server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br> |
| 95 | admin-user: exampleuser (password is "yoWgDR42iv")><br> |
| 96 | </tt></b> |
| 97 | </blockquote> |
| 98 | |
| 99 | <p>If the remote repository requires a login, include a |
| 100 | userid in the URL like this: |
| 101 | |
| 102 | <blockquote> |
| 103 | <b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b> |
| 104 | </blockquote> |
| 105 | |
| 106 | <p>You will be prompted separately for the password. |
| 107 | Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. |
| 108 | For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") </p> |
| 109 | |
| 110 | <p>If you are behind a restrictive firewall, you might need |
| 111 | to <a href="#proxy">specify an HTTP proxy</a>.</p> |
| 112 | |
| 113 | <p>A Fossil repository is a single disk file. Instead of cloning, |
| 114 | you can just make a copy of the repository file (for example, using |
| 115 | "scp"). Note, however, that the repository file contains auxiliary |
| 116 | information above and beyond the versioned files, including some |
| 117 | sensitive information such as password hashes and email addresses. If you |
| 118 | want to share Fossil repositories directly by copying, consider running the |
| @@ -119,11 +120,11 @@ | |
| 119 | [/help/scrub|fossil scrub] command to remove sensitive information |
| 120 | before transmitting the file. |
| 121 | |
| 122 | <h2 id="import">Importing From Another Version Control System</h2> |
| 123 | |
| 124 | <p>Rather than start a new project, or clone an existing Fossil project, |
| 125 | you might prefer to |
| 126 | <a href="./inout.wiki">import an existing Git project</a> |
| 127 | into Fossil using the [/help/import | fossil import] command. |
| 128 | |
| 129 | You can even decide to export your project back into git using the |
| @@ -137,14 +138,14 @@ | |
| 137 | [https://www.mercurial-scm.org/|Mercurial]. |
| 138 | Fossil can also import [https://subversion.apache.org/|Subversion projects] directly. |
| 139 | |
| 140 | <h2 id="checkout">Checking Out A Local Tree</h2> |
| 141 | |
| 142 | <p>To work on a project in fossil, you need to check out a local |
| 143 | copy of the source tree. Create the directory you want to be |
| 144 | the root of your tree and cd into that directory. Then |
| 145 | do this: ([/help/open | more info])</p> |
| 146 | |
| 147 | <blockquote> |
| 148 | <b>fossil open </b><i> repository-filename</i> |
| 149 | </blockquote> |
| 150 | |
| @@ -160,15 +161,15 @@ | |
| 160 | </tt></b> |
| 161 | </blockquote> |
| 162 | |
| 163 | (or "fossil open ..\myclone.fossil" on Windows). |
| 164 | |
| 165 | <p>This leaves you with the newest version of the tree |
| 166 | checked out. |
| 167 | From anywhere underneath the root of your local tree, you |
| 168 | can type commands like the following to find out the status of |
| 169 | your local tree:</p> |
| 170 | |
| 171 | <blockquote> |
| 172 | <b>[/help/info | fossil info]</b><br> |
| 173 | <b>[/help/status | fossil status]</b><br> |
| 174 | <b>[/help/changes | fossil changes]</b><br> |
| @@ -176,60 +177,63 @@ | |
| 176 | <b>[/help/timeline | fossil timeline]</b><br> |
| 177 | <b>[/help/ls | fossil ls]</b><br> |
| 178 | <b>[/help/branch | fossil branch]</b><br> |
| 179 | </blockquote> |
| 180 | |
| 181 | <p>If you created a new repository using "fossil init" some commands will not |
| 182 | produce much output.</p> |
| 183 | |
| 184 | <p>Note that Fossil allows you to make multiple check-outs in |
| 185 | separate directories from the same repository. This enables you, |
| 186 | for example, to do builds from multiple branches or versions at |
| 187 | the same time without having to generate extra clones.</p> |
| 188 | |
| 189 | <p>To switch a checkout between different versions and branches, |
| 190 | use:</p> |
| 191 | |
| 192 | <blockquote> |
| 193 | <b>[/help/update | fossil update]</b><br> |
| 194 | <b>[/help/checkout | fossil checkout]</b><br> |
| 195 | </blockquote> |
| 196 | |
| 197 | <p>[/help/update | update] honors the "autosync" option and |
| 198 | does a "soft" switch, merging any local changes into the target |
| 199 | version, whereas [/help/checkout | checkout] does not |
| 200 | automatically sync and does a "hard" switch, overwriting local |
| 201 | changes if told to do so.</p> |
| 202 | |
| 203 | <h2 id="changes">Making and Committing Changes</h2> |
| 204 | |
| 205 | <p>To add new files to your project or remove existing ones, use these |
| 206 | commands:</p> |
| 207 | |
| 208 | <blockquote> |
| 209 | <b>[/help/add | fossil add]</b> <i>file...</i><br> |
| 210 | <b>[/help/rm | fossil rm]</b> <i>file...</i><br> |
| 211 | <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br> |
| 212 | </blockquote> |
| 213 | |
| 214 | <p>The command:</p> |
| 215 | <blockquote> |
| 216 | <b> |
| 217 | [/help/changes | fossil changes]</b> |
| 218 | </blockquote> |
| 219 | <p>lists files that have changed since the last commit to the repository. For |
| 220 | example, if you edit the file "README.md":</p> |
| 221 | |
| 222 | <blockquote> |
| 223 | <b> |
| 224 | fossil changes<br> |
| 225 | EDITED README.md |
| 226 | </b> |
| 227 | </blockquote> |
| 228 | |
| 229 | <p>To see exactly what change was made you can use the command</p> |
| 230 | [/help/diff | fossil diff]: |
| 231 | <blockquote> |
| 232 | <b> |
| 233 | fossil diff <br><tt> |
| 234 | Index: README.md<br> |
| 235 | ============================================================<br> |
| @@ -239,17 +243,19 @@ | |
| 239 | +Made some changes to the project<br> |
| 240 | # Original text<br> |
| 241 | </tt></b> |
| 242 | </blockquote> |
| 243 | |
| 244 | <p>"fossil diff" is the difference between your tree on disk now and as the tree was |
| 245 | when you did "fossil open". An open is the first checkout from a repository |
| 246 | into a new directory. </p> |
| 247 | |
| 248 | <p>To see the most recent changes made to the repository by other users, use "fossil timeline" to |
| 249 | find out the most recent commit, and then "fossil diff" between that commit and the |
| 250 | current tree: </p> |
| 251 | <blockquote> |
| 252 | <b> |
| 253 | fossil timeline <br><tt> |
| 254 | === 2021-03-28 === <br> |
| 255 | 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br> |
| @@ -269,11 +275,11 @@ | |
| 269 | </blockquote> |
| 270 | |
| 271 | "current" is an alias for the checkout version, so the command |
| 272 | "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. |
| 273 | |
| 274 | <p>To commit your changes to a local-only repository:</p> |
| 275 | <blockquote> |
| 276 | <b> |
| 277 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 278 | # Enter a commit message for this check-in. Lines beginning with # are ignored.<br> |
| 279 | #<br> |
| @@ -284,163 +290,136 @@ | |
| 284 | Edited file to add description of code changes<br> |
| 285 | New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br> |
| 286 | </tt></b> |
| 287 | </blockquote> |
| 288 | |
| 289 | <p>You will be prompted for check-in comments using whatever editor |
| 290 | is specified by your VISUAL or EDITOR environment variable. If none is |
| 291 | specified Fossil uses line-editing in the terminal.</p> |
| 292 | |
| 293 | <p>To commit your changes to a repository that was cloned from remote you |
| 294 | perform the same actions but the results are different. Fossil |
| 295 | defaults to 'autosync' mode, a single-stage commit that sends all changes |
| 296 | committed to the local repository immediately on to the remote parent repository. This |
| 297 | only works if you have write permission to the remote respository.</p> |
| 298 | |
| 299 | <h2 id="naming">Naming of Files, Checkins, and Branches</h2> |
| 300 | |
| 301 | <p>Fossil deals with information artifacts. This Quickstart document only deals |
| 302 | with files and collections of files, but be aware there are also tickets, wiki pages and more. |
| 303 | Every artifact in Fossil has a universally-unique hash id, and may also have a |
| 304 | human-readable name.</p> |
| 305 | |
| 306 | <p>The following are all equivalent ways of identifying a Fossil file, |
| 307 | checkin or branch artifact:</p> |
| 308 | |
| 309 | <ul> |
| 310 | <li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a |
| 311 | <li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters |
| 312 | <li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash |
| 313 | </ul> |
| 314 | |
| 315 | <p>A special convenience branch is "trunk", which is Fossil's default branch name for |
| 316 | the first checkin, and the default for any time a branch name is needed but not |
| 317 | specified.</p> |
| 318 | |
| 319 | This will get you started on identifying checkins. The |
| 320 | <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including |
| 321 | how timestamps can also be used. |
| 322 | |
| 323 | <h2 id="config">Configuring Your Local Repository</h2> |
| 324 | |
| 325 | <p>When you create a new repository, either by cloning an existing |
| 326 | project or create a new project of your own, you usually want to do some |
| 327 | local configuration. This is easily accomplished using the web-server |
| 328 | that is built into fossil. Start the fossil web server like this: |
| 329 | ([/help/ui | more info])</p> |
| 330 | |
| 331 | <blockquote> |
| 332 | <b>fossil ui </b><i> repository-filename</i> |
| 333 | </blockquote> |
| 334 | |
| 335 | <p>You can omit the <i>repository-filename</i> from the command above |
| 336 | if you are inside a checked-out local tree.</p> |
| 337 | |
| 338 | <p>This starts a web server then automatically launches your |
| 339 | web browser and makes it point to this web server. If your system |
| 340 | has an unusual configuration, fossil might not be able to figure out |
| 341 | how to start your web browser. In that case, first tell fossil |
| 342 | where to find your web browser using a command like this:</p> |
| 343 | |
| 344 | <blockquote> |
| 345 | <b>fossil setting web-browser </b><i> path-to-web-browser</i> |
| 346 | </blockquote> |
| 347 | |
| 348 | <p>By default, fossil does not require a login for HTTP connections |
| 349 | coming in from the IP loopback address 127.0.0.1. You can, and perhaps |
| 350 | should, change this after you create a few users.</p> |
| 351 | |
| 352 | <p>When you are finished configuring, just press Control-C or use |
| 353 | the <b>kill</b> command to shut down the mini-server.</p> |
| 354 | |
| 355 | <h2 id="changes">Making Changes</h2> |
| 356 | |
| 357 | <p>To add new files to your project, or remove old files, use these |
| 358 | commands:</p> |
| 359 | |
| 360 | <blockquote> |
| 361 | <b>[/help/add | fossil add]</b> <i>file...</i><br> |
| 362 | <b>[/help/rm | fossil rm]</b> <i>file...</i><br> |
| 363 | <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br> |
| 364 | </blockquote> |
| 365 | |
| 366 | <p>You can also edit files freely. Once you are ready to commit |
| 367 | your changes, type:</p> |
| 368 | |
| 369 | <blockquote> |
| 370 | <b>[/help/commit | fossil commit]</b> |
| 371 | </blockquote> |
| 372 | |
| 373 | <p>You will be prompted for check-in comments using whatever editor |
| 374 | is specified by your VISUAL or EDITOR environment variable.</p> |
| 375 | |
| 376 | In the default configuration, the [/help/commit|commit] |
| 377 | command will also automatically [/help/push|push] your changes, but that |
| 378 | feature can be disabled. (More information about |
| 379 | [./concepts.wiki#workflow|autosync] and how to disable it.) |
| 380 | Remember that your coworkers can not see your changes until you |
| 381 | commit and push them.</p> |
| 382 | |
| 383 | <h2 id="sharing">Sharing Changes</h2> |
| 384 | |
| 385 | <p>When [./concepts.wiki#workflow|autosync] is turned off, |
| 386 | the changes you [/help/commit | commit] are only |
| 387 | on your local repository. |
| 388 | To share those changes with other repositories, do:</p> |
| 389 | |
| 390 | <blockquote> |
| 391 | <b>[/help/push | fossil push]</b> <i>URL</i> |
| 392 | </blockquote> |
| 393 | |
| 394 | <p>Where <i>URL</i> is the http: URL of the server repository you |
| 395 | want to share your changes with. If you omit the <i>URL</i> argument, |
| 396 | fossil will use whatever server you most recently synced with.</p> |
| 397 | |
| 398 | <p>The [/help/push | push] command only sends your changes to others. To |
| 399 | Receive changes from others, use [/help/pull | pull]. Or go both ways at |
| 400 | once using [/help/sync | sync]:</p> |
| 401 | |
| 402 | <blockquote> |
| 403 | <b>[/help/pull | fossil pull]</b> <i>URL</i><br> |
| 404 | <b>[/help/sync | fossil sync]</b> <i>URL</i> |
| 405 | </blockquote> |
| 406 | |
| 407 | <p>When you pull in changes from others, they go into your repository, |
| 408 | not into your checked-out local tree. To get the changes into your |
| 409 | local tree, use [/help/update | update]:</p> |
| 410 | |
| 411 | <blockquote> |
| 412 | <b>[/help/update | fossil update]</b> <i>VERSION</i> |
| 413 | </blockquote> |
| 414 | |
| 415 | <p>The <i>VERSION</i> can be the name of a branch or tag or any |
| 416 | abbreviation to the 40-character |
| 417 | artifact identifier for a particular check-in, or it can be a |
| 418 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 419 | If you omit |
| 420 | the <i>VERSION</i>, then fossil moves you to the |
| 421 | latest version of the branch your are currently on.</p> |
| 422 | |
| 423 | <p>The default behavior is for [./concepts.wiki#workflow|autosync] to |
| 424 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 425 | when you run [/help/update|update] and a [/help/push|push] happens |
| 426 | automatically after you [/help/commit|commit]. So in normal practice, |
| 427 | the push, pull, and sync commands are rarely used. But it is important |
| 428 | to know about them, all the same.</p> |
| 429 | |
| 430 | <blockquote> |
| 431 | <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> |
| 432 | </blockquote> |
| 433 | |
| 434 | <p>Is similar to update except that it does not honor the autosync |
| 435 | setting, nor does it merge in local changes - it prefers to overwrite |
| 436 | them and fails if local changes exist unless the <tt>--force</tt> |
| 437 | flag is used.</p> |
| 438 | |
| 439 | <h2 id="branch" name="merge">Branching And Merging</h2> |
| 440 | |
| 441 | <p>Use the --branch option to the [/help/commit | commit] command |
| 442 | to start a new branch. Note that in Fossil, branches are normally |
| 443 | created when you commit, not before you start editing. You can |
| 444 | use the [/help/branch | branch new] command to create a new branch |
| 445 | before you start editing, if you want, but most people just wait |
| 446 | until they are ready to commit. |
| @@ -447,25 +426,25 @@ | |
| 447 | |
| 448 | To merge two branches back together, first |
| 449 | [/help/update | update] to the branch you want to merge into. |
| 450 | Then do a [/help/merge|merge] of the other branch that you want to incorporate |
| 451 | the changes from. For example, to merge "featureX" changes into "trunk" |
| 452 | do this:</p> |
| 453 | |
| 454 | <blockquote> |
| 455 | <b>fossil [/help/update|update] trunk</b><br> |
| 456 | <b>fossil [/help/merge|merge] featureX</b><br> |
| 457 | <i># make sure the merge didn't break anything...</i><br> |
| 458 | <b>fossil [/help/commit|commit] |
| 459 | </blockquote> |
| 460 | |
| 461 | <p>The argument to the [/help/merge|merge] command can be any of the |
| 462 | version identifier forms that work for [/help/update|update]. |
| 463 | ([./checkin_names.wiki|more info].) |
| 464 | The merge command has options to cherry-pick individual |
| 465 | changes, or to back out individual changes, if you don't want to |
| 466 | do a full merge.</p> |
| 467 | |
| 468 | The merge command puts all changes in your working check-out. |
| 469 | No changes are made to the repository. |
| 470 | You must run [/help/commit|commit] separately |
| 471 | to add the merge changes into your repository to make them persistent |
| @@ -478,110 +457,110 @@ | |
| 478 | multiple merges. So even if you have merged the featureX branch |
| 479 | into trunk previously, you can do so again and Fossil will automatically |
| 480 | know to pull in only those changes that have occurred since the previous |
| 481 | merge. |
| 482 | |
| 483 | <p>If a merge or update doesn't work out (perhaps something breaks or |
| 484 | there are many merge conflicts) then you back up using:</p> |
| 485 | |
| 486 | <blockquote> |
| 487 | <b>[/help/undo | fossil undo]</b> |
| 488 | </blockquote> |
| 489 | |
| 490 | <p>This will back out the changes that the merge or update made to the |
| 491 | working checkout. There is also a [/help/redo|redo] command if you undo by |
| 492 | mistake. Undo and redo only work for changes that have |
| 493 | not yet been checked in using commit and there is only a single |
| 494 | level of undo/redo.</p> |
| 495 | |
| 496 | |
| 497 | <h2 id="server">Setting Up A Server</h2> |
| 498 | |
| 499 | <p>Fossil can act as a stand-alone web server using one of these |
| 500 | commands:</p> |
| 501 | |
| 502 | <blockquote> |
| 503 | <b>[/help/server | fossil server]</b> <i>repository-filename</i><br> |
| 504 | <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> |
| 505 | </blockquote> |
| 506 | |
| 507 | <p>The <i>repository-filename</i> can be omitted when these commands |
| 508 | are run from within an open check-out, which is a particularly useful |
| 509 | shortcut with the <b>fossil ui</b> command. |
| 510 | |
| 511 | <p>The <b>ui</b> command is intended for accessing the web user interface |
| 512 | from a local desktop. (We sometimes call this mode "Fossil UI.") |
| 513 | The <b>ui</b> command differs from the |
| 514 | <b>server</b> command by binding to the loopback IP |
| 515 | address only (thus making the web UI visible only on the |
| 516 | local machine) and by automatically starting your default web browser, |
| 517 | pointing it at the running UI |
| 518 | server. The localhost restriction exists because it also gives anyone |
| 519 | who can access the resulting web UI full control over the |
| 520 | repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful |
| 521 | Setup capabliity].)</p> |
| 522 | |
| 523 | <p>For cross-machine collaboration, use the <b>server</b> command instead, |
| 524 | which binds on all IP addresses, does not try to start a web browser, |
| 525 | and enforces [./caps/ | Fossil's role-based access control system].</p> |
| 526 | |
| 527 | <p>Servers are also easily configured as: |
| 528 | |
| 529 | <ul> |
| 530 | <li>[./server/any/inetd.md|inetd] |
| 531 | <li>[./server/debian/service.md|systemd] |
| 532 | <li>[./server/any/cgi.md|CGI] |
| 533 | <li>[./server/any/scgi.md|SCGI] |
| 534 | </ul> |
| 535 | |
| 536 | <p>…along with [./server/#matrix | several other options].</p> |
| 537 | |
| 538 | <p>The [./selfhost.wiki | self-hosting fossil repositories] use |
| 539 | CGI. |
| 540 | |
| 541 | <p>You might <i>need</i> to set up a server, whether you know it yet or |
| 542 | not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server] |
| 543 | article for details.</p> |
| 544 | |
| 545 | <h2 id="proxy">HTTP Proxies</h2> |
| 546 | |
| 547 | <p>If you are behind a restrictive firewall that requires you to use |
| 548 | an HTTP proxy to reach the internet, then you can configure the proxy |
| 549 | in three different ways. You can tell fossil about your proxy using |
| 550 | a command-line option on commands that use the network, |
| 551 | <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.</p> |
| 552 | |
| 553 | <blockquote> |
| 554 | <b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i> |
| 555 | </blockquote> |
| 556 | |
| 557 | <p>It is annoying to have to type in the proxy URL every time you |
| 558 | sync your project, though, so you can make the proxy configuration |
| 559 | persistent using the [/help/setting | setting] command:</p> |
| 560 | |
| 561 | <blockquote> |
| 562 | <b>fossil setting proxy </b><i>Proxy-URL</i> |
| 563 | </blockquote> |
| 564 | |
| 565 | <p>Or, you can set the "<b>http_proxy</b>" environment variable:</p> |
| 566 | |
| 567 | <blockquote> |
| 568 | <b>export http_proxy=</b><i>Proxy-URL</i> |
| 569 | </blockquote> |
| 570 | |
| 571 | <p>To stop using the proxy, do:</p> |
| 572 | |
| 573 | <blockquote> |
| 574 | <b>fossil setting proxy off</b> |
| 575 | </blockquote> |
| 576 | |
| 577 | <p>Or unset the environment variable. The fossil setting for the |
| 578 | HTTP proxy takes precedence over the environment variable and the |
| 579 | command-line option overrides both. If you have a persistent |
| 580 | proxy setting that you want to override for a one-time sync, that |
| 581 | is easily done on the command-line. For example, to sync with |
| 582 | a co-worker's repository on your LAN, you might type:</p> |
| 583 | |
| 584 | <blockquote> |
| 585 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 586 | </blockquote> |
| 587 | |
| 588 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -1,20 +1,21 @@ | |
| 1 | <title>Fossil Quick Start Guide</title> |
| 2 | <h1 align="center">Fossil Quick Start</h1> |
| 3 | |
| 4 | This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly |
| 5 | and painlessly. |
| 6 | |
| 7 | <h2 id="install">Installing</h2> |
| 8 | |
| 9 | Fossil is a single self-contained C program. You need to |
| 10 | either download a |
| 11 | [https://fossil-scm.org/home/uv/download.html|precompiled |
| 12 | binary] |
| 13 | or <a href="build.wiki">compile it yourself</a> from sources. |
| 14 | Install Fossil by putting the fossil binary |
| 15 | someplace on your $PATH. |
| 16 | |
| 17 | You can test that Fossil is present and working like this: |
| 18 | |
| 19 | <blockquote> |
| 20 | <b> |
| 21 | fossil version<br> |
| @@ -22,15 +23,15 @@ | |
| 23 | </b> |
| 24 | </blockquote> |
| 25 | |
| 26 | <h2 id="workflow" name="fslclone">General Work Flow</h2> |
| 27 | |
| 28 | Fossil works with repository files (a database in a single file with the project's |
| 29 | complete history) and with checked-out local trees (the working directory |
| 30 | you use to do your work). |
| 31 | (See [./glossary.md | the glossary] for more background.) |
| 32 | The workflow looks like this: |
| 33 | |
| 34 | <ul> |
| 35 | <li>Create or clone a repository file. ([/help/init|fossil init] or |
| 36 | [/help/clone | fossil clone]) |
| 37 | <li>Check out a local tree. ([/help/open | fossil open]) |
| @@ -39,17 +40,17 @@ | |
| 40 | </ul> |
| 41 | |
| 42 | Fossil can be entirely driven from the command line. Many features |
| 43 | can also be conveniently accessed from the built-in web user interface. |
| 44 | |
| 45 | The following sections give a brief overview of these |
| 46 | operations. |
| 47 | |
| 48 | <h2 id="new">Starting A New Project</h2> |
| 49 | |
| 50 | To start a new project with fossil create a new empty repository |
| 51 | this way: ([/help/init | more info]) |
| 52 | |
| 53 | <blockquote> |
| 54 | <b>fossil init </b><i> repository-filename</i> |
| 55 | </blockquote> |
| 56 | |
| @@ -57,23 +58,23 @@ | |
| 58 | The <tt>.fossil</tt> extension is traditional but only required if you are going to use the |
| 59 | <tt>[/help/server | fossil server DIRECTORY]</tt> feature.” |
| 60 | |
| 61 | <h2 id="clone">Cloning An Existing Repository</h2> |
| 62 | |
| 63 | Most fossil operations interact with a repository that is on the |
| 64 | local disk drive, not on a remote system. Hence, before accessing |
| 65 | a remote repository it is necessary to make a local copy of that |
| 66 | repository. Making a local copy of a remote repository is called |
| 67 | "cloning". |
| 68 | |
| 69 | Clone a remote repository as follows: ([/help/clone | more info]) |
| 70 | |
| 71 | <blockquote> |
| 72 | <b>fossil clone</b> <i>URL repository-filename</i> |
| 73 | </blockquote> |
| 74 | |
| 75 | The <i>URL</i> specifies the fossil repository |
| 76 | you want to clone. The <i>repository-filename</i> is the new local |
| 77 | filename into which the cloned repository will be written. For |
| 78 | example, to clone the source code of Fossil itself: |
| 79 | |
| 80 | <blockquote> |
| @@ -94,25 +95,25 @@ | |
| 95 | server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br> |
| 96 | admin-user: exampleuser (password is "yoWgDR42iv")><br> |
| 97 | </tt></b> |
| 98 | </blockquote> |
| 99 | |
| 100 | If the remote repository requires a login, include a |
| 101 | userid in the URL like this: |
| 102 | |
| 103 | <blockquote> |
| 104 | <b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b> |
| 105 | </blockquote> |
| 106 | |
| 107 | You will be prompted separately for the password. |
| 108 | Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. |
| 109 | For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") |
| 110 | |
| 111 | If you are behind a restrictive firewall, you might need |
| 112 | to <a href="#proxy">specify an HTTP proxy</a>. |
| 113 | |
| 114 | A Fossil repository is a single disk file. Instead of cloning, |
| 115 | you can just make a copy of the repository file (for example, using |
| 116 | "scp"). Note, however, that the repository file contains auxiliary |
| 117 | information above and beyond the versioned files, including some |
| 118 | sensitive information such as password hashes and email addresses. If you |
| 119 | want to share Fossil repositories directly by copying, consider running the |
| @@ -119,11 +120,11 @@ | |
| 120 | [/help/scrub|fossil scrub] command to remove sensitive information |
| 121 | before transmitting the file. |
| 122 | |
| 123 | <h2 id="import">Importing From Another Version Control System</h2> |
| 124 | |
| 125 | Rather than start a new project, or clone an existing Fossil project, |
| 126 | you might prefer to |
| 127 | <a href="./inout.wiki">import an existing Git project</a> |
| 128 | into Fossil using the [/help/import | fossil import] command. |
| 129 | |
| 130 | You can even decide to export your project back into git using the |
| @@ -137,14 +138,14 @@ | |
| 138 | [https://www.mercurial-scm.org/|Mercurial]. |
| 139 | Fossil can also import [https://subversion.apache.org/|Subversion projects] directly. |
| 140 | |
| 141 | <h2 id="checkout">Checking Out A Local Tree</h2> |
| 142 | |
| 143 | To work on a project in fossil, you need to check out a local |
| 144 | copy of the source tree. Create the directory you want to be |
| 145 | the root of your tree and cd into that directory. Then |
| 146 | do this: ([/help/open | more info]) |
| 147 | |
| 148 | <blockquote> |
| 149 | <b>fossil open </b><i> repository-filename</i> |
| 150 | </blockquote> |
| 151 | |
| @@ -160,15 +161,15 @@ | |
| 161 | </tt></b> |
| 162 | </blockquote> |
| 163 | |
| 164 | (or "fossil open ..\myclone.fossil" on Windows). |
| 165 | |
| 166 | This leaves you with the newest version of the tree |
| 167 | checked out. |
| 168 | From anywhere underneath the root of your local tree, you |
| 169 | can type commands like the following to find out the status of |
| 170 | your local tree: |
| 171 | |
| 172 | <blockquote> |
| 173 | <b>[/help/info | fossil info]</b><br> |
| 174 | <b>[/help/status | fossil status]</b><br> |
| 175 | <b>[/help/changes | fossil changes]</b><br> |
| @@ -176,60 +177,63 @@ | |
| 177 | <b>[/help/timeline | fossil timeline]</b><br> |
| 178 | <b>[/help/ls | fossil ls]</b><br> |
| 179 | <b>[/help/branch | fossil branch]</b><br> |
| 180 | </blockquote> |
| 181 | |
| 182 | If you created a new repository using "fossil init" some commands will not |
| 183 | produce much output. |
| 184 | |
| 185 | Note that Fossil allows you to make multiple check-outs in |
| 186 | separate directories from the same repository. This enables you, |
| 187 | for example, to do builds from multiple branches or versions at |
| 188 | the same time without having to generate extra clones. |
| 189 | |
| 190 | To switch a checkout between different versions and branches, |
| 191 | use:< |
| 192 | |
| 193 | <blockquote> |
| 194 | <b>[/help/update | fossil update]</b><br> |
| 195 | <b>[/help/checkout | fossil checkout]</b><br> |
| 196 | </blockquote> |
| 197 | |
| 198 | [/help/update | update] honors the "autosync" option and |
| 199 | does a "soft" switch, merging any local changes into the target |
| 200 | version, whereas [/help/checkout | checkout] does not |
| 201 | automatically sync and does a "hard" switch, overwriting local |
| 202 | changes if told to do so. |
| 203 | |
| 204 | <h2 id="changes">Making and Committing Changes</h2> |
| 205 | |
| 206 | To add new files to your project or remove existing ones, use these |
| 207 | commands: |
| 208 | |
| 209 | <blockquote> |
| 210 | <b>[/help/add | fossil add]</b> <i>file...</i><br> |
| 211 | <b>[/help/rm | fossil rm]</b> <i>file...</i><br> |
| 212 | <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br> |
| 213 | </blockquote> |
| 214 | |
| 215 | The command: |
| 216 | |
| 217 | <blockquote> |
| 218 | <b> |
| 219 | [/help/changes | fossil changes]</b> |
| 220 | </blockquote> |
| 221 | |
| 222 | lists files that have changed since the last commit to the repository. For |
| 223 | example, if you edit the file "README.md": |
| 224 | |
| 225 | <blockquote> |
| 226 | <b> |
| 227 | fossil changes<br> |
| 228 | EDITED README.md |
| 229 | </b> |
| 230 | </blockquote> |
| 231 | |
| 232 | To see exactly what change was made you can use the command |
| 233 | <b>[/help/diff | fossil diff]</b>: |
| 234 | |
| 235 | <blockquote> |
| 236 | <b> |
| 237 | fossil diff <br><tt> |
| 238 | Index: README.md<br> |
| 239 | ============================================================<br> |
| @@ -239,17 +243,19 @@ | |
| 243 | +Made some changes to the project<br> |
| 244 | # Original text<br> |
| 245 | </tt></b> |
| 246 | </blockquote> |
| 247 | |
| 248 | "fossil diff" shows the difference between your tree on disk now and as |
| 249 | the tree was when you last committed changes. If you haven't committed |
| 250 | yet, then it shows the difference relative to the tip-of-trunk commit in |
| 251 | the repository, being what you get when you "fossil open" a repository |
| 252 | without specifying a version, populating the working directory. |
| 253 | |
| 254 | To see the most recent changes made to the repository by other users, use "fossil timeline" to |
| 255 | find out the most recent commit, and then "fossil diff" between that commit and the |
| 256 | current tree: |
| 257 | <blockquote> |
| 258 | <b> |
| 259 | fossil timeline <br><tt> |
| 260 | === 2021-03-28 === <br> |
| 261 | 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br> |
| @@ -269,11 +275,11 @@ | |
| 275 | </blockquote> |
| 276 | |
| 277 | "current" is an alias for the checkout version, so the command |
| 278 | "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. |
| 279 | |
| 280 | To commit your changes to a local-only repository: |
| 281 | <blockquote> |
| 282 | <b> |
| 283 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 284 | # Enter a commit message for this check-in. Lines beginning with # are ignored.<br> |
| 285 | #<br> |
| @@ -284,163 +290,136 @@ | |
| 290 | Edited file to add description of code changes<br> |
| 291 | New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br> |
| 292 | </tt></b> |
| 293 | </blockquote> |
| 294 | |
| 295 | You will be prompted for check-in comments using whatever editor |
| 296 | is specified by your VISUAL or EDITOR environment variable. If none is |
| 297 | specified Fossil uses line-editing in the terminal. |
| 298 | |
| 299 | To commit your changes to a repository that was cloned from a remote |
| 300 | repository, you give the same command, but the results are different. |
| 301 | Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a |
| 302 | single-stage commit that sends all changes committed to the local |
| 303 | repository immediately on to the remote parent repository. This only |
| 304 | works if you have write permission to the remote respository. |
| 305 | |
| 306 | <h2 id="naming">Naming of Files, Checkins, and Branches</h2> |
| 307 | |
| 308 | Fossil deals with information artifacts. This Quickstart document only deals |
| 309 | with files and collections of files, but be aware there are also tickets, wiki pages and more. |
| 310 | Every artifact in Fossil has a universally-unique hash id, and may also have a |
| 311 | human-readable name. |
| 312 | |
| 313 | The following are all equivalent ways of identifying a Fossil file, |
| 314 | checkin or branch artifact: |
| 315 | |
| 316 | <ul> |
| 317 | <li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a |
| 318 | <li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters |
| 319 | <li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash |
| 320 | </ul> |
| 321 | |
| 322 | A special convenience branch is "trunk", which is Fossil's default branch name for |
| 323 | the first checkin, and the default for any time a branch name is needed but not |
| 324 | specified. |
| 325 | |
| 326 | This will get you started on identifying checkins. The |
| 327 | <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including |
| 328 | how timestamps can also be used. |
| 329 | |
| 330 | <h2 id="config">Configuring Your Local Repository</h2> |
| 331 | |
| 332 | When you create a new repository, either by cloning an existing |
| 333 | project or create a new project of your own, you usually want to do some |
| 334 | local configuration. This is easily accomplished using the web-server |
| 335 | that is built into fossil. Start the fossil web server like this: |
| 336 | ([/help/ui | more info]) |
| 337 | |
| 338 | <blockquote> |
| 339 | <b>fossil ui </b><i> repository-filename</i> |
| 340 | </blockquote> |
| 341 | |
| 342 | You can omit the <i>repository-filename</i> from the command above |
| 343 | if you are inside a checked-out local tree. |
| 344 | |
| 345 | This starts a web server then automatically launches your |
| 346 | web browser and makes it point to this web server. If your system |
| 347 | has an unusual configuration, fossil might not be able to figure out |
| 348 | how to start your web browser. In that case, first tell fossil |
| 349 | where to find your web browser using a command like this: |
| 350 | |
| 351 | <blockquote> |
| 352 | <b>fossil setting web-browser </b><i> path-to-web-browser</i> |
| 353 | </blockquote> |
| 354 | |
| 355 | By default, fossil does not require a login for HTTP connections |
| 356 | coming in from the IP loopback address 127.0.0.1. You can, and perhaps |
| 357 | should, change this after you create a few users. |
| 358 | |
| 359 | When you are finished configuring, just press Control-C or use |
| 360 | the <b>kill</b> command to shut down the mini-server. |
| 361 | |
| 362 | <h2 id="sharing">Sharing Changes</h2> |
| 363 | |
| 364 | When [./concepts.wiki#workflow|autosync] is turned off, |
| 365 | the changes you [/help/commit | commit] are only |
| 366 | on your local repository. |
| 367 | To share those changes with other repositories, do: |
| 368 | |
| 369 | <blockquote> |
| 370 | <b>[/help/push | fossil push]</b> <i>URL</i> |
| 371 | </blockquote> |
| 372 | |
| 373 | Where <i>URL</i> is the http: URL of the server repository you |
| 374 | want to share your changes with. If you omit the <i>URL</i> argument, |
| 375 | fossil will use whatever server you most recently synced with. |
| 376 | |
| 377 | The [/help/push | push] command only sends your changes to others. To |
| 378 | Receive changes from others, use [/help/pull | pull]. Or go both ways at |
| 379 | once using [/help/sync | sync]: |
| 380 | |
| 381 | <blockquote> |
| 382 | <b>[/help/pull | fossil pull]</b> <i>URL</i><br> |
| 383 | <b>[/help/sync | fossil sync]</b> <i>URL</i> |
| 384 | </blockquote> |
| 385 | |
| 386 | When you pull in changes from others, they go into your repository, |
| 387 | not into your checked-out local tree. To get the changes into your |
| 388 | local tree, use [/help/update | update]: |
| 389 | |
| 390 | <blockquote> |
| 391 | <b>[/help/update | fossil update]</b> <i>VERSION</i> |
| 392 | </blockquote> |
| 393 | |
| 394 | The <i>VERSION</i> can be the name of a branch or tag or any |
| 395 | abbreviation to the 40-character |
| 396 | artifact identifier for a particular check-in, or it can be a |
| 397 | date/time stamp. ([./checkin_names.wiki | more info]) |
| 398 | If you omit |
| 399 | the <i>VERSION</i>, then fossil moves you to the |
| 400 | latest version of the branch your are currently on. |
| 401 | |
| 402 | The default behavior is for [./concepts.wiki#workflow|autosync] to |
| 403 | be turned on. That means that a [/help/pull|pull] automatically occurs |
| 404 | when you run [/help/update|update] and a [/help/push|push] happens |
| 405 | automatically after you [/help/commit|commit]. So in normal practice, |
| 406 | the push, pull, and sync commands are rarely used. But it is important |
| 407 | to know about them, all the same. |
| 408 | |
| 409 | <blockquote> |
| 410 | <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> |
| 411 | </blockquote> |
| 412 | |
| 413 | Is similar to update except that it does not honor the autosync |
| 414 | setting, nor does it merge in local changes - it prefers to overwrite |
| 415 | them and fails if local changes exist unless the <tt>--force</tt> |
| 416 | flag is used. |
| 417 | |
| 418 | <h2 id="branch" name="merge">Branching And Merging</h2> |
| 419 | |
| 420 | Use the --branch option to the [/help/commit | commit] command |
| 421 | to start a new branch. Note that in Fossil, branches are normally |
| 422 | created when you commit, not before you start editing. You can |
| 423 | use the [/help/branch | branch new] command to create a new branch |
| 424 | before you start editing, if you want, but most people just wait |
| 425 | until they are ready to commit. |
| @@ -447,25 +426,25 @@ | |
| 426 | |
| 427 | To merge two branches back together, first |
| 428 | [/help/update | update] to the branch you want to merge into. |
| 429 | Then do a [/help/merge|merge] of the other branch that you want to incorporate |
| 430 | the changes from. For example, to merge "featureX" changes into "trunk" |
| 431 | do this: |
| 432 | |
| 433 | <blockquote> |
| 434 | <b>fossil [/help/update|update] trunk</b><br> |
| 435 | <b>fossil [/help/merge|merge] featureX</b><br> |
| 436 | <i># make sure the merge didn't break anything...</i><br> |
| 437 | <b>fossil [/help/commit|commit] |
| 438 | </blockquote> |
| 439 | |
| 440 | The argument to the [/help/merge|merge] command can be any of the |
| 441 | version identifier forms that work for [/help/update|update]. |
| 442 | ([./checkin_names.wiki|more info].) |
| 443 | The merge command has options to cherry-pick individual |
| 444 | changes, or to back out individual changes, if you don't want to |
| 445 | do a full merge. |
| 446 | |
| 447 | The merge command puts all changes in your working check-out. |
| 448 | No changes are made to the repository. |
| 449 | You must run [/help/commit|commit] separately |
| 450 | to add the merge changes into your repository to make them persistent |
| @@ -478,110 +457,110 @@ | |
| 457 | multiple merges. So even if you have merged the featureX branch |
| 458 | into trunk previously, you can do so again and Fossil will automatically |
| 459 | know to pull in only those changes that have occurred since the previous |
| 460 | merge. |
| 461 | |
| 462 | If a merge or update doesn't work out (perhaps something breaks or |
| 463 | there are many merge conflicts) then you back up using: |
| 464 | |
| 465 | <blockquote> |
| 466 | <b>[/help/undo | fossil undo]</b> |
| 467 | </blockquote> |
| 468 | |
| 469 | This will back out the changes that the merge or update made to the |
| 470 | working checkout. There is also a [/help/redo|redo] command if you undo by |
| 471 | mistake. Undo and redo only work for changes that have |
| 472 | not yet been checked in using commit and there is only a single |
| 473 | level of undo/redo. |
| 474 | |
| 475 | |
| 476 | <h2 id="server">Setting Up A Server</h2> |
| 477 | |
| 478 | Fossil can act as a stand-alone web server using one of these |
| 479 | commands: |
| 480 | |
| 481 | <blockquote> |
| 482 | <b>[/help/server | fossil server]</b> <i>repository-filename</i><br> |
| 483 | <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> |
| 484 | </blockquote> |
| 485 | |
| 486 | The <i>repository-filename</i> can be omitted when these commands |
| 487 | are run from within an open check-out, which is a particularly useful |
| 488 | shortcut with the <b>fossil ui</b> command. |
| 489 | |
| 490 | The <b>ui</b> command is intended for accessing the web user interface |
| 491 | from a local desktop. (We sometimes call this mode "Fossil UI.") |
| 492 | The <b>ui</b> command differs from the |
| 493 | <b>server</b> command by binding to the loopback IP |
| 494 | address only (thus making the web UI visible only on the |
| 495 | local machine) and by automatically starting your default web browser, |
| 496 | pointing it at the running UI |
| 497 | server. The localhost restriction exists because it also gives anyone |
| 498 | who can access the resulting web UI full control over the |
| 499 | repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful |
| 500 | Setup capabliity].) |
| 501 | |
| 502 | For cross-machine collaboration, use the <b>server</b> command instead, |
| 503 | which binds on all IP addresses, does not try to start a web browser, |
| 504 | and enforces [./caps/ | Fossil's role-based access control system]. |
| 505 | |
| 506 | Servers are also easily configured as: |
| 507 | |
| 508 | <ul> |
| 509 | <li>[./server/any/inetd.md|inetd] |
| 510 | <li>[./server/debian/service.md|systemd] |
| 511 | <li>[./server/any/cgi.md|CGI] |
| 512 | <li>[./server/any/scgi.md|SCGI] |
| 513 | </ul> |
| 514 | |
| 515 | …along with [./server/#matrix | several other options]. |
| 516 | |
| 517 | The [./selfhost.wiki | self-hosting fossil repositories] use |
| 518 | CGI. |
| 519 | |
| 520 | You might <i>need</i> to set up a server, whether you know it yet or |
| 521 | not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server] |
| 522 | article for details. |
| 523 | |
| 524 | <h2 id="proxy">HTTP Proxies</h2> |
| 525 | |
| 526 | If you are behind a restrictive firewall that requires you to use |
| 527 | an HTTP proxy to reach the internet, then you can configure the proxy |
| 528 | in three different ways. You can tell fossil about your proxy using |
| 529 | a command-line option on commands that use the network, |
| 530 | <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>. |
| 531 | |
| 532 | <blockquote> |
| 533 | <b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i> |
| 534 | </blockquote> |
| 535 | |
| 536 | It is annoying to have to type in the proxy URL every time you |
| 537 | sync your project, though, so you can make the proxy configuration |
| 538 | persistent using the [/help/setting | setting] command: |
| 539 | |
| 540 | <blockquote> |
| 541 | <b>fossil setting proxy </b><i>Proxy-URL</i> |
| 542 | </blockquote> |
| 543 | |
| 544 | Or, you can set the "<b>http_proxy</b>" environment variable: |
| 545 | |
| 546 | <blockquote> |
| 547 | <b>export http_proxy=</b><i>Proxy-URL</i> |
| 548 | </blockquote> |
| 549 | |
| 550 | To stop using the proxy, do: |
| 551 | |
| 552 | <blockquote> |
| 553 | <b>fossil setting proxy off</b> |
| 554 | </blockquote> |
| 555 | |
| 556 | Or unset the environment variable. The fossil setting for the |
| 557 | HTTP proxy takes precedence over the environment variable and the |
| 558 | command-line option overrides both. If you have a persistent |
| 559 | proxy setting that you want to override for a one-time sync, that |
| 560 | is easily done on the command-line. For example, to sync with |
| 561 | a co-worker's repository on your LAN, you might type: |
| 562 | |
| 563 | <blockquote> |
| 564 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> |
| 565 | </blockquote> |
| 566 | |
| 567 |
+2
-2
| --- www/quotes.wiki | ||
| +++ www/quotes.wiki | ||
| @@ -145,12 +145,12 @@ | ||
| 145 | 145 | </blockquote> |
| 146 | 146 | |
| 147 | 147 | <li>In the fossil community - and hence in fossil itself - development history |
| 148 | 148 | is pretty much sacrosanct. The very name "fossil" was to chosen to |
| 149 | 149 | reflect the unchanging nature of things in that history. |
| 150 | - | |
| 151 | -<p>In git (or rather, the git community), the development history is part of | |
| 150 | +<br><br> | |
| 151 | +In git (or rather, the git community), the development history is part of | |
| 152 | 152 | the published aspect of the project, so it provides tools for rearranging |
| 153 | 153 | that history so you can present what you "should" have done rather |
| 154 | 154 | than what you actually did. |
| 155 | 155 | |
| 156 | 156 | <blockquote> |
| 157 | 157 |
| --- www/quotes.wiki | |
| +++ www/quotes.wiki | |
| @@ -145,12 +145,12 @@ | |
| 145 | </blockquote> |
| 146 | |
| 147 | <li>In the fossil community - and hence in fossil itself - development history |
| 148 | is pretty much sacrosanct. The very name "fossil" was to chosen to |
| 149 | reflect the unchanging nature of things in that history. |
| 150 | |
| 151 | <p>In git (or rather, the git community), the development history is part of |
| 152 | the published aspect of the project, so it provides tools for rearranging |
| 153 | that history so you can present what you "should" have done rather |
| 154 | than what you actually did. |
| 155 | |
| 156 | <blockquote> |
| 157 |
| --- www/quotes.wiki | |
| +++ www/quotes.wiki | |
| @@ -145,12 +145,12 @@ | |
| 145 | </blockquote> |
| 146 | |
| 147 | <li>In the fossil community - and hence in fossil itself - development history |
| 148 | is pretty much sacrosanct. The very name "fossil" was to chosen to |
| 149 | reflect the unchanging nature of things in that history. |
| 150 | <br><br> |
| 151 | In git (or rather, the git community), the development history is part of |
| 152 | the published aspect of the project, so it provides tools for rearranging |
| 153 | that history so you can present what you "should" have done rather |
| 154 | than what you actually did. |
| 155 | |
| 156 | <blockquote> |
| 157 |
+3
-2
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -198,15 +198,16 @@ | ||
| 198 | 198 | |
| 199 | 199 | But that argument is comparing apples to oranges, since the two diffs |
| 200 | 200 | do not have the same baseline. The correct way to see only the feature |
| 201 | 201 | branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). |
| 202 | 202 | |
| 203 | -<center><table border="1" cellpadding="5" cellspacing="0"> | |
| 203 | +<table border="1" cellpadding="5" cellspacing="0" | |
| 204 | + style="margin-left:auto; margin-right:auto"> | |
| 204 | 205 | <tr><th>Rebase<th>Merge<th>What You See |
| 205 | 206 | <tr><td>diff(C2,C5\')<td>diff(C2,C7)<td>Commingled branch and mainline changes |
| 206 | 207 | <tr><td>diff(C6,C5\')<td>diff(C6,C7)<td>Branch changes only |
| 207 | -</table></center> | |
| 208 | +</table> | |
| 208 | 209 | |
| 209 | 210 | Remember: C7 and C5\' are bit-for-bit identical, so the output of the |
| 210 | 211 | diff is not determined by whether you select C7 or C5\' as the target |
| 211 | 212 | of the diff, but rather by your choice of the diff source, C2 or C6. |
| 212 | 213 | |
| 213 | 214 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -198,15 +198,16 @@ | |
| 198 | |
| 199 | But that argument is comparing apples to oranges, since the two diffs |
| 200 | do not have the same baseline. The correct way to see only the feature |
| 201 | branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). |
| 202 | |
| 203 | <center><table border="1" cellpadding="5" cellspacing="0"> |
| 204 | <tr><th>Rebase<th>Merge<th>What You See |
| 205 | <tr><td>diff(C2,C5\')<td>diff(C2,C7)<td>Commingled branch and mainline changes |
| 206 | <tr><td>diff(C6,C5\')<td>diff(C6,C7)<td>Branch changes only |
| 207 | </table></center> |
| 208 | |
| 209 | Remember: C7 and C5\' are bit-for-bit identical, so the output of the |
| 210 | diff is not determined by whether you select C7 or C5\' as the target |
| 211 | of the diff, but rather by your choice of the diff source, C2 or C6. |
| 212 | |
| 213 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -198,15 +198,16 @@ | |
| 198 | |
| 199 | But that argument is comparing apples to oranges, since the two diffs |
| 200 | do not have the same baseline. The correct way to see only the feature |
| 201 | branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). |
| 202 | |
| 203 | <table border="1" cellpadding="5" cellspacing="0" |
| 204 | style="margin-left:auto; margin-right:auto"> |
| 205 | <tr><th>Rebase<th>Merge<th>What You See |
| 206 | <tr><td>diff(C2,C5\')<td>diff(C2,C7)<td>Commingled branch and mainline changes |
| 207 | <tr><td>diff(C6,C5\')<td>diff(C6,C7)<td>Branch changes only |
| 208 | </table> |
| 209 | |
| 210 | Remember: C7 and C5\' are bit-for-bit identical, so the output of the |
| 211 | diff is not determined by whether you select C7 or C5\' as the target |
| 212 | of the diff, but rather by your choice of the diff source, C2 or C6. |
| 213 | |
| 214 |
+4
-4
| --- www/server/any/althttpd.md | ||
| +++ www/server/any/althttpd.md | ||
| @@ -6,14 +6,14 @@ | ||
| 6 | 6 | security, ease of configuration, and low resource usage. |
| 7 | 7 | |
| 8 | 8 | To set up a Fossil server as CGI on a host running the althttpd web |
| 9 | 9 | server, follow these steps. |
| 10 | 10 | <ol> |
| 11 | -<li<p>Get the althttpd webserver running on the host. This is easily | |
| 11 | +<li>Get the althttpd webserver running on the host. This is easily | |
| 12 | 12 | done by following the [althttpd documentation][althttpd]. |
| 13 | 13 | |
| 14 | -<li><p>Create a CGI script for your Fossil repository. The script will | |
| 14 | +<li>Create a CGI script for your Fossil repository. The script will | |
| 15 | 15 | be typically be two lines of code that look something like this: |
| 16 | 16 | |
| 17 | 17 | ~~~ |
| 18 | 18 | #!/usr/bin/fossil |
| 19 | 19 | repository: /home/yourlogin/fossils/project.fossil |
| @@ -23,13 +23,13 @@ | ||
| 23 | 23 | CGI script accepts [other options][cgi] besides the |
| 24 | 24 | repository:" line. You can add in other options as you desire, |
| 25 | 25 | but the single "repository:" line is normally all that is needed |
| 26 | 26 | to get started. |
| 27 | 27 | |
| 28 | -<li><p>Make the CGI script executable. | |
| 28 | +<li>Make the CGI script executable. | |
| 29 | 29 | |
| 30 | -<li><p>Verify that the fossil repository file and the directory that contains | |
| 30 | +<li>Verify that the fossil repository file and the directory that contains | |
| 31 | 31 | the repository are both writable by whatever user the web server is |
| 32 | 32 | running and. |
| 33 | 33 | </ol> |
| 34 | 34 | |
| 35 | 35 | And you are done. Visit the URL that corresponds to the CGI script |
| 36 | 36 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -6,14 +6,14 @@ | |
| 6 | security, ease of configuration, and low resource usage. |
| 7 | |
| 8 | To set up a Fossil server as CGI on a host running the althttpd web |
| 9 | server, follow these steps. |
| 10 | <ol> |
| 11 | <li<p>Get the althttpd webserver running on the host. This is easily |
| 12 | done by following the [althttpd documentation][althttpd]. |
| 13 | |
| 14 | <li><p>Create a CGI script for your Fossil repository. The script will |
| 15 | be typically be two lines of code that look something like this: |
| 16 | |
| 17 | ~~~ |
| 18 | #!/usr/bin/fossil |
| 19 | repository: /home/yourlogin/fossils/project.fossil |
| @@ -23,13 +23,13 @@ | |
| 23 | CGI script accepts [other options][cgi] besides the |
| 24 | repository:" line. You can add in other options as you desire, |
| 25 | but the single "repository:" line is normally all that is needed |
| 26 | to get started. |
| 27 | |
| 28 | <li><p>Make the CGI script executable. |
| 29 | |
| 30 | <li><p>Verify that the fossil repository file and the directory that contains |
| 31 | the repository are both writable by whatever user the web server is |
| 32 | running and. |
| 33 | </ol> |
| 34 | |
| 35 | And you are done. Visit the URL that corresponds to the CGI script |
| 36 |
| --- www/server/any/althttpd.md | |
| +++ www/server/any/althttpd.md | |
| @@ -6,14 +6,14 @@ | |
| 6 | security, ease of configuration, and low resource usage. |
| 7 | |
| 8 | To set up a Fossil server as CGI on a host running the althttpd web |
| 9 | server, follow these steps. |
| 10 | <ol> |
| 11 | <li>Get the althttpd webserver running on the host. This is easily |
| 12 | done by following the [althttpd documentation][althttpd]. |
| 13 | |
| 14 | <li>Create a CGI script for your Fossil repository. The script will |
| 15 | be typically be two lines of code that look something like this: |
| 16 | |
| 17 | ~~~ |
| 18 | #!/usr/bin/fossil |
| 19 | repository: /home/yourlogin/fossils/project.fossil |
| @@ -23,13 +23,13 @@ | |
| 23 | CGI script accepts [other options][cgi] besides the |
| 24 | repository:" line. You can add in other options as you desire, |
| 25 | but the single "repository:" line is normally all that is needed |
| 26 | to get started. |
| 27 | |
| 28 | <li>Make the CGI script executable. |
| 29 | |
| 30 | <li>Verify that the fossil repository file and the directory that contains |
| 31 | the repository are both writable by whatever user the web server is |
| 32 | running and. |
| 33 | </ol> |
| 34 | |
| 35 | And you are done. Visit the URL that corresponds to the CGI script |
| 36 |
+66
-33
| --- www/server/debian/nginx.md | ||
| +++ www/server/debian/nginx.md | ||
| @@ -142,46 +142,79 @@ | ||
| 142 | 142 | contains the configuration for `*.foo.net`. |
| 143 | 143 | |
| 144 | 144 | The configuration for our `example.com` web site, stored in |
| 145 | 145 | `/etc/nginx/sites-enabled/local/example.com` is: |
| 146 | 146 | |
| 147 | - server { | |
| 148 | - server_name .example.com .example.net ""; | |
| 149 | - include local/generic; | |
| 150 | - | |
| 151 | - access_log /var/log/nginx/example.com-https-access.log; | |
| 152 | - error_log /var/log/nginx/example.com-https-error.log; | |
| 153 | - | |
| 154 | - # Bypass Fossil for the static documentation generated from | |
| 155 | - # our source code by Doxygen, so it merges into the embedded | |
| 156 | - # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that | |
| 157 | - # these generated files actually be stored in the repo. This | |
| 158 | - # also lets us set aggressive caching on these docs, since | |
| 159 | - # they rarely change. | |
| 160 | - location /code/doc/html { | |
| 161 | - root /var/www/example.com/code/doc/html; | |
| 162 | - | |
| 163 | - location ~* \.(html|ico|css|js|gif|jpg|png)$ { | |
| 164 | - expires 7d; | |
| 165 | - add_header Vary Accept-Encoding; | |
| 166 | - access_log off; | |
| 167 | - } | |
| 168 | - } | |
| 169 | - | |
| 170 | - # Redirect everything else to the Fossil instance | |
| 171 | - location /code { | |
| 172 | - include scgi_params; | |
| 173 | - scgi_param SCRIPT_NAME "/code"; | |
| 174 | - scgi_pass 127.0.0.1:12345; | |
| 175 | - } | |
| 176 | - } | |
| 147 | +---- | |
| 148 | + | |
| 149 | + server { | |
| 150 | + server_name .example.com .example.net ""; | |
| 151 | + include local/generic; | |
| 152 | + include local/code; | |
| 153 | + | |
| 154 | + access_log /var/log/nginx/example.com-https-access.log; | |
| 155 | + error_log /var/log/nginx/example.com-https-error.log; | |
| 156 | + | |
| 157 | + # Bypass Fossil for the static documentation generated from | |
| 158 | + # our source code by Doxygen, so it merges into the embedded | |
| 159 | + # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that | |
| 160 | + # these generated files actually be stored in the repo. This | |
| 161 | + # also lets us set aggressive caching on these docs, since | |
| 162 | + # they rarely change. | |
| 163 | + location /code/doc/html { | |
| 164 | + root /var/www/example.com/code/doc/html; | |
| 165 | + | |
| 166 | + location ~* \.(html|ico|css|js|gif|jpg|png)$ { | |
| 167 | + add_header Vary Accept-Encoding; | |
| 168 | + access_log off; | |
| 169 | + expires 7d; | |
| 170 | + } | |
| 171 | + } | |
| 172 | + | |
| 173 | + # Redirect everything under /code to the Fossil instance | |
| 174 | + location /code { | |
| 175 | + include local/code; | |
| 176 | + | |
| 177 | + # Extended caching for URLs that include unique IDs | |
| 178 | + location ~ "/(artifact|doc|file|raw)/[0-9a-f]{40,64}" { | |
| 179 | + add_header Cache-Control "public, max-age=31536000, immutable"; | |
| 180 | + include local/code; | |
| 181 | + access_log off; | |
| 182 | + } | |
| 183 | + | |
| 184 | + # Lesser caching for URLs likely to be quasi-static | |
| 185 | + location ~* \.(css|gif|ico|js|jpg|png)$ { | |
| 186 | + add_header Vary Accept-Encoding; | |
| 187 | + include local/code; | |
| 188 | + access_log off; | |
| 189 | + expires 7d; | |
| 190 | + } | |
| 191 | + } | |
| 192 | + } | |
| 193 | + | |
| 194 | +---- | |
| 177 | 195 | |
| 178 | 196 | As you can see, this is a pure extension of [the basic nginx service |
| 179 | 197 | configuration for SCGI][scgii], showing off a few ideas you might want to |
| 180 | 198 | try on your own site, such as static asset proxying. |
| 181 | 199 | |
| 182 | -The `local/generic` file referenced above helps us reduce unnecessary | |
| 200 | +You also need a `local/code` file containing: | |
| 201 | + | |
| 202 | + include scgi_params; | |
| 203 | + scgi_pass 127.0.0.1:12345; | |
| 204 | + scgi_param SCRIPT_NAME "/code"; | |
| 205 | + | |
| 206 | +We separate that out because nginx refuses to inherit certain settings | |
| 207 | +between nested location blocks, so rather than repeat them, we extract | |
| 208 | +them to this separate file and include it from both locations where it’s | |
| 209 | +needed. You see this above where we set far-future expiration dates on | |
| 210 | +files served by Fossil via URLs that contain hashes that change when the | |
| 211 | +content changes. It tells your browser that the content of these URLs | |
| 212 | +can never change without the URL itself changing, which makes your | |
| 213 | +Fossil-based site considerably faster. | |
| 214 | + | |
| 215 | +Similarly, the `local/generic` file referenced above helps us reduce unnecessary | |
| 183 | 216 | repetition among the multiple sites this configuration hosts: |
| 184 | 217 | |
| 185 | 218 | root /var/www/$host; |
| 186 | 219 | |
| 187 | 220 | listen 80; |
| @@ -189,12 +222,12 @@ | ||
| 189 | 222 | |
| 190 | 223 | charset utf-8; |
| 191 | 224 | |
| 192 | 225 | There are some configuration directives that nginx refuses to substitute |
| 193 | 226 | variables into, citing performance considerations, so there is a limit |
| 194 | -to how much repetition you can squeeze out this way. One such example is | |
| 195 | -the `access_log` and `error_log` directives, which follow an obvious | |
| 227 | +to how much repetition you can squeeze out this way. One such example | |
| 228 | +are the `access_log` and `error_log` directives, which follow an obvious | |
| 196 | 229 | pattern from one host to the next. Sadly, you must tolerate some |
| 197 | 230 | repetition across `server { }` blocks when setting up multiple domains |
| 198 | 231 | on a single server. |
| 199 | 232 | |
| 200 | 233 | The configuration for `foo.net` is similar. |
| 201 | 234 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -142,46 +142,79 @@ | |
| 142 | contains the configuration for `*.foo.net`. |
| 143 | |
| 144 | The configuration for our `example.com` web site, stored in |
| 145 | `/etc/nginx/sites-enabled/local/example.com` is: |
| 146 | |
| 147 | server { |
| 148 | server_name .example.com .example.net ""; |
| 149 | include local/generic; |
| 150 | |
| 151 | access_log /var/log/nginx/example.com-https-access.log; |
| 152 | error_log /var/log/nginx/example.com-https-error.log; |
| 153 | |
| 154 | # Bypass Fossil for the static documentation generated from |
| 155 | # our source code by Doxygen, so it merges into the embedded |
| 156 | # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that |
| 157 | # these generated files actually be stored in the repo. This |
| 158 | # also lets us set aggressive caching on these docs, since |
| 159 | # they rarely change. |
| 160 | location /code/doc/html { |
| 161 | root /var/www/example.com/code/doc/html; |
| 162 | |
| 163 | location ~* \.(html|ico|css|js|gif|jpg|png)$ { |
| 164 | expires 7d; |
| 165 | add_header Vary Accept-Encoding; |
| 166 | access_log off; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | # Redirect everything else to the Fossil instance |
| 171 | location /code { |
| 172 | include scgi_params; |
| 173 | scgi_param SCRIPT_NAME "/code"; |
| 174 | scgi_pass 127.0.0.1:12345; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | As you can see, this is a pure extension of [the basic nginx service |
| 179 | configuration for SCGI][scgii], showing off a few ideas you might want to |
| 180 | try on your own site, such as static asset proxying. |
| 181 | |
| 182 | The `local/generic` file referenced above helps us reduce unnecessary |
| 183 | repetition among the multiple sites this configuration hosts: |
| 184 | |
| 185 | root /var/www/$host; |
| 186 | |
| 187 | listen 80; |
| @@ -189,12 +222,12 @@ | |
| 189 | |
| 190 | charset utf-8; |
| 191 | |
| 192 | There are some configuration directives that nginx refuses to substitute |
| 193 | variables into, citing performance considerations, so there is a limit |
| 194 | to how much repetition you can squeeze out this way. One such example is |
| 195 | the `access_log` and `error_log` directives, which follow an obvious |
| 196 | pattern from one host to the next. Sadly, you must tolerate some |
| 197 | repetition across `server { }` blocks when setting up multiple domains |
| 198 | on a single server. |
| 199 | |
| 200 | The configuration for `foo.net` is similar. |
| 201 |
| --- www/server/debian/nginx.md | |
| +++ www/server/debian/nginx.md | |
| @@ -142,46 +142,79 @@ | |
| 142 | contains the configuration for `*.foo.net`. |
| 143 | |
| 144 | The configuration for our `example.com` web site, stored in |
| 145 | `/etc/nginx/sites-enabled/local/example.com` is: |
| 146 | |
| 147 | ---- |
| 148 | |
| 149 | server { |
| 150 | server_name .example.com .example.net ""; |
| 151 | include local/generic; |
| 152 | include local/code; |
| 153 | |
| 154 | access_log /var/log/nginx/example.com-https-access.log; |
| 155 | error_log /var/log/nginx/example.com-https-error.log; |
| 156 | |
| 157 | # Bypass Fossil for the static documentation generated from |
| 158 | # our source code by Doxygen, so it merges into the embedded |
| 159 | # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that |
| 160 | # these generated files actually be stored in the repo. This |
| 161 | # also lets us set aggressive caching on these docs, since |
| 162 | # they rarely change. |
| 163 | location /code/doc/html { |
| 164 | root /var/www/example.com/code/doc/html; |
| 165 | |
| 166 | location ~* \.(html|ico|css|js|gif|jpg|png)$ { |
| 167 | add_header Vary Accept-Encoding; |
| 168 | access_log off; |
| 169 | expires 7d; |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | # Redirect everything under /code to the Fossil instance |
| 174 | location /code { |
| 175 | include local/code; |
| 176 | |
| 177 | # Extended caching for URLs that include unique IDs |
| 178 | location ~ "/(artifact|doc|file|raw)/[0-9a-f]{40,64}" { |
| 179 | add_header Cache-Control "public, max-age=31536000, immutable"; |
| 180 | include local/code; |
| 181 | access_log off; |
| 182 | } |
| 183 | |
| 184 | # Lesser caching for URLs likely to be quasi-static |
| 185 | location ~* \.(css|gif|ico|js|jpg|png)$ { |
| 186 | add_header Vary Accept-Encoding; |
| 187 | include local/code; |
| 188 | access_log off; |
| 189 | expires 7d; |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | ---- |
| 195 | |
| 196 | As you can see, this is a pure extension of [the basic nginx service |
| 197 | configuration for SCGI][scgii], showing off a few ideas you might want to |
| 198 | try on your own site, such as static asset proxying. |
| 199 | |
| 200 | You also need a `local/code` file containing: |
| 201 | |
| 202 | include scgi_params; |
| 203 | scgi_pass 127.0.0.1:12345; |
| 204 | scgi_param SCRIPT_NAME "/code"; |
| 205 | |
| 206 | We separate that out because nginx refuses to inherit certain settings |
| 207 | between nested location blocks, so rather than repeat them, we extract |
| 208 | them to this separate file and include it from both locations where it’s |
| 209 | needed. You see this above where we set far-future expiration dates on |
| 210 | files served by Fossil via URLs that contain hashes that change when the |
| 211 | content changes. It tells your browser that the content of these URLs |
| 212 | can never change without the URL itself changing, which makes your |
| 213 | Fossil-based site considerably faster. |
| 214 | |
| 215 | Similarly, the `local/generic` file referenced above helps us reduce unnecessary |
| 216 | repetition among the multiple sites this configuration hosts: |
| 217 | |
| 218 | root /var/www/$host; |
| 219 | |
| 220 | listen 80; |
| @@ -189,12 +222,12 @@ | |
| 222 | |
| 223 | charset utf-8; |
| 224 | |
| 225 | There are some configuration directives that nginx refuses to substitute |
| 226 | variables into, citing performance considerations, so there is a limit |
| 227 | to how much repetition you can squeeze out this way. One such example |
| 228 | are the `access_log` and `error_log` directives, which follow an obvious |
| 229 | pattern from one host to the next. Sadly, you must tolerate some |
| 230 | repetition across `server { }` blocks when setting up multiple domains |
| 231 | on a single server. |
| 232 | |
| 233 | The configuration for `foo.net` is similar. |
| 234 |
+9
-10
| --- www/server/debian/service.md | ||
| +++ www/server/debian/service.md | ||
| @@ -12,16 +12,18 @@ | ||
| 12 | 12 | |
| 13 | 13 | Two of the methods for running [containerized Fossil][cntdoc] integrate |
| 14 | 14 | with `systemd`, potentially obviating the more direct methods below: |
| 15 | 15 | |
| 16 | 16 | * If you take [the Podman method][podman] of running containerized |
| 17 | - Fossil, it opens the `podman create` option for you, exemplified in [the | |
| 18 | - `fslsrv` script][fslsrv] used on this author’s public Fossil-based | |
| 19 | - web site. That script pulls [custom containers][tscnt] from [my Docker | |
| 20 | - Hub repo][dhrepo]. With these elements combined, this scheme allows you to build | |
| 21 | - from source on one machine, then deploy to a server, running Fossil | |
| 22 | - containerized without needing build tools on the server. | |
| 17 | + Fossil, it opens the `podman generate systemd` option for you, as | |
| 18 | + exemplified in [the `fslsrv` script][fslsrv] used on this author’s | |
| 19 | + public Fossil-based web site. That script pulls its container images | |
| 20 | + from [my Docker Hub repo][dhrepo] to avoid the need for my public | |
| 21 | + Fossil server to have build tools and a copy of the Fossil source | |
| 22 | + tree. You’re welcome to use my images as-is, or you may use these | |
| 23 | + tools to bounce custom builds up through a separate container image | |
| 24 | + repo you manage. | |
| 23 | 25 | |
| 24 | 26 | * If you’re willing to give up [a lot of features][nsweak] relative to |
| 25 | 27 | Podman, and you’re willing to tolerate a lot more manual |
| 26 | 28 | administrivia, [the nspawn method][nspawn] has a lot less overhead, |
| 27 | 29 | being a direct feature of `systemd` itself. |
| @@ -34,11 +36,10 @@ | ||
| 34 | 36 | [dhrepo]: https://hub.docker.com/r/tangentsoft/fossil |
| 35 | 37 | [fslsrv]: https://tangentsoft.com/fossil/dir?name=bin |
| 36 | 38 | [nspawn]: ../../containers.md#nspawn |
| 37 | 39 | [nsweak]: ../../containers.md#nspawn-weaknesses |
| 38 | 40 | [podman]: ../../containers.md#podman |
| 39 | -[tscnt]: https://tangentsoft.com/fossil/dir/container | |
| 40 | 41 | |
| 41 | 42 | |
| 42 | 43 | ## User Service |
| 43 | 44 | |
| 44 | 45 | A fun thing you can easily do with `systemd` that you can’t directly do |
| @@ -202,17 +203,15 @@ | ||
| 202 | 203 | |
| 203 | 204 | [Install] |
| 204 | 205 | WantedBy=multi-user.target |
| 205 | 206 | ``` |
| 206 | 207 | |
| 207 | -We’ll explain the “`@`” in the file name below. | |
| 208 | - | |
| 209 | 208 | Notice that we haven’t told `systemd` which user and group to run Fossil |
| 210 | 209 | under. Since this is a system-level service definition, that means it |
| 211 | 210 | will run as root, which then causes Fossil to [automatically drop into a |
| 212 | 211 | `chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory` |
| 213 | -we’ve configured above, shortly each `fossil http` call starts. | |
| 212 | +we’ve configured above, shortly after each `fossil http` call starts. | |
| 214 | 213 | |
| 215 | 214 | The `Restart*` directives we had in the user service configuration above |
| 216 | 215 | are unnecessary for this method, since Fossil isn’t supposed to remain |
| 217 | 216 | running under it. Each HTTP hit starts one Fossil instance, which |
| 218 | 217 | handles that single client’s request and then immediately shuts down. |
| 219 | 218 |
| --- www/server/debian/service.md | |
| +++ www/server/debian/service.md | |
| @@ -12,16 +12,18 @@ | |
| 12 | |
| 13 | Two of the methods for running [containerized Fossil][cntdoc] integrate |
| 14 | with `systemd`, potentially obviating the more direct methods below: |
| 15 | |
| 16 | * If you take [the Podman method][podman] of running containerized |
| 17 | Fossil, it opens the `podman create` option for you, exemplified in [the |
| 18 | `fslsrv` script][fslsrv] used on this author’s public Fossil-based |
| 19 | web site. That script pulls [custom containers][tscnt] from [my Docker |
| 20 | Hub repo][dhrepo]. With these elements combined, this scheme allows you to build |
| 21 | from source on one machine, then deploy to a server, running Fossil |
| 22 | containerized without needing build tools on the server. |
| 23 | |
| 24 | * If you’re willing to give up [a lot of features][nsweak] relative to |
| 25 | Podman, and you’re willing to tolerate a lot more manual |
| 26 | administrivia, [the nspawn method][nspawn] has a lot less overhead, |
| 27 | being a direct feature of `systemd` itself. |
| @@ -34,11 +36,10 @@ | |
| 34 | [dhrepo]: https://hub.docker.com/r/tangentsoft/fossil |
| 35 | [fslsrv]: https://tangentsoft.com/fossil/dir?name=bin |
| 36 | [nspawn]: ../../containers.md#nspawn |
| 37 | [nsweak]: ../../containers.md#nspawn-weaknesses |
| 38 | [podman]: ../../containers.md#podman |
| 39 | [tscnt]: https://tangentsoft.com/fossil/dir/container |
| 40 | |
| 41 | |
| 42 | ## User Service |
| 43 | |
| 44 | A fun thing you can easily do with `systemd` that you can’t directly do |
| @@ -202,17 +203,15 @@ | |
| 202 | |
| 203 | [Install] |
| 204 | WantedBy=multi-user.target |
| 205 | ``` |
| 206 | |
| 207 | We’ll explain the “`@`” in the file name below. |
| 208 | |
| 209 | Notice that we haven’t told `systemd` which user and group to run Fossil |
| 210 | under. Since this is a system-level service definition, that means it |
| 211 | will run as root, which then causes Fossil to [automatically drop into a |
| 212 | `chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory` |
| 213 | we’ve configured above, shortly each `fossil http` call starts. |
| 214 | |
| 215 | The `Restart*` directives we had in the user service configuration above |
| 216 | are unnecessary for this method, since Fossil isn’t supposed to remain |
| 217 | running under it. Each HTTP hit starts one Fossil instance, which |
| 218 | handles that single client’s request and then immediately shuts down. |
| 219 |
| --- www/server/debian/service.md | |
| +++ www/server/debian/service.md | |
| @@ -12,16 +12,18 @@ | |
| 12 | |
| 13 | Two of the methods for running [containerized Fossil][cntdoc] integrate |
| 14 | with `systemd`, potentially obviating the more direct methods below: |
| 15 | |
| 16 | * If you take [the Podman method][podman] of running containerized |
| 17 | Fossil, it opens the `podman generate systemd` option for you, as |
| 18 | exemplified in [the `fslsrv` script][fslsrv] used on this author’s |
| 19 | public Fossil-based web site. That script pulls its container images |
| 20 | from [my Docker Hub repo][dhrepo] to avoid the need for my public |
| 21 | Fossil server to have build tools and a copy of the Fossil source |
| 22 | tree. You’re welcome to use my images as-is, or you may use these |
| 23 | tools to bounce custom builds up through a separate container image |
| 24 | repo you manage. |
| 25 | |
| 26 | * If you’re willing to give up [a lot of features][nsweak] relative to |
| 27 | Podman, and you’re willing to tolerate a lot more manual |
| 28 | administrivia, [the nspawn method][nspawn] has a lot less overhead, |
| 29 | being a direct feature of `systemd` itself. |
| @@ -34,11 +36,10 @@ | |
| 36 | [dhrepo]: https://hub.docker.com/r/tangentsoft/fossil |
| 37 | [fslsrv]: https://tangentsoft.com/fossil/dir?name=bin |
| 38 | [nspawn]: ../../containers.md#nspawn |
| 39 | [nsweak]: ../../containers.md#nspawn-weaknesses |
| 40 | [podman]: ../../containers.md#podman |
| 41 | |
| 42 | |
| 43 | ## User Service |
| 44 | |
| 45 | A fun thing you can easily do with `systemd` that you can’t directly do |
| @@ -202,17 +203,15 @@ | |
| 203 | |
| 204 | [Install] |
| 205 | WantedBy=multi-user.target |
| 206 | ``` |
| 207 | |
| 208 | Notice that we haven’t told `systemd` which user and group to run Fossil |
| 209 | under. Since this is a system-level service definition, that means it |
| 210 | will run as root, which then causes Fossil to [automatically drop into a |
| 211 | `chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory` |
| 212 | we’ve configured above, shortly after each `fossil http` call starts. |
| 213 | |
| 214 | The `Restart*` directives we had in the user service configuration above |
| 215 | are unnecessary for this method, since Fossil isn’t supposed to remain |
| 216 | running under it. Each HTTP hit starts one Fossil instance, which |
| 217 | handles that single client’s request and then immediately shuts down. |
| 218 |
+2
-2
| --- www/server/index.html | ||
| +++ www/server/index.html | ||
| @@ -117,11 +117,11 @@ | ||
| 117 | 117 | <li><a href="any/scgi.md">SCGI</a> |
| 118 | 118 | <li><a href="#ssh">SSH</a> |
| 119 | 119 | </ol> |
| 120 | 120 | |
| 121 | 121 | <p>All of these methods can serve either a single repository or a |
| 122 | -directory hierarchy containing mulitiple repositories.</p> | |
| 122 | +directory hierarchy containing multiple repositories.</p> | |
| 123 | 123 | |
| 124 | 124 | <p>You are not restricted to a single server setup. The same Fossil |
| 125 | 125 | repository can be served using two or more of the above techniques at |
| 126 | 126 | the same time. These methods use clean, well-defined, standard |
| 127 | 127 | interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from |
| @@ -171,11 +171,11 @@ | ||
| 171 | 171 | dispatches a copy of itself to deal with each incoming request. You can |
| 172 | 172 | expose Fossil directly to the clients in this way or you can interpose a |
| 173 | 173 | <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> |
| 174 | 174 | layer between the clients and Fossil.</p> |
| 175 | 175 | |
| 176 | -<h3 id="scgi"3>SCGI</h3> | |
| 176 | +<h3 id="scgi">SCGI</h3> | |
| 177 | 177 | |
| 178 | 178 | <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. |
| 179 | 179 | When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is |
| 180 | 180 | run with the extra <tt>--scgi</tt> option, it listens for incoming |
| 181 | 181 | SCGI requests rather than HTTP requests. This allows Fossil to |
| 182 | 182 |
| --- www/server/index.html | |
| +++ www/server/index.html | |
| @@ -117,11 +117,11 @@ | |
| 117 | <li><a href="any/scgi.md">SCGI</a> |
| 118 | <li><a href="#ssh">SSH</a> |
| 119 | </ol> |
| 120 | |
| 121 | <p>All of these methods can serve either a single repository or a |
| 122 | directory hierarchy containing mulitiple repositories.</p> |
| 123 | |
| 124 | <p>You are not restricted to a single server setup. The same Fossil |
| 125 | repository can be served using two or more of the above techniques at |
| 126 | the same time. These methods use clean, well-defined, standard |
| 127 | interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from |
| @@ -171,11 +171,11 @@ | |
| 171 | dispatches a copy of itself to deal with each incoming request. You can |
| 172 | expose Fossil directly to the clients in this way or you can interpose a |
| 173 | <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> |
| 174 | layer between the clients and Fossil.</p> |
| 175 | |
| 176 | <h3 id="scgi"3>SCGI</h3> |
| 177 | |
| 178 | <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. |
| 179 | When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is |
| 180 | run with the extra <tt>--scgi</tt> option, it listens for incoming |
| 181 | SCGI requests rather than HTTP requests. This allows Fossil to |
| 182 |
| --- www/server/index.html | |
| +++ www/server/index.html | |
| @@ -117,11 +117,11 @@ | |
| 117 | <li><a href="any/scgi.md">SCGI</a> |
| 118 | <li><a href="#ssh">SSH</a> |
| 119 | </ol> |
| 120 | |
| 121 | <p>All of these methods can serve either a single repository or a |
| 122 | directory hierarchy containing multiple repositories.</p> |
| 123 | |
| 124 | <p>You are not restricted to a single server setup. The same Fossil |
| 125 | repository can be served using two or more of the above techniques at |
| 126 | the same time. These methods use clean, well-defined, standard |
| 127 | interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from |
| @@ -171,11 +171,11 @@ | |
| 171 | dispatches a copy of itself to deal with each incoming request. You can |
| 172 | expose Fossil directly to the clients in this way or you can interpose a |
| 173 | <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> |
| 174 | layer between the clients and Fossil.</p> |
| 175 | |
| 176 | <h3 id="scgi">SCGI</h3> |
| 177 | |
| 178 | <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. |
| 179 | When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is |
| 180 | run with the extra <tt>--scgi</tt> option, it listens for incoming |
| 181 | SCGI requests rather than HTTP requests. This allows Fossil to |
| 182 |
+22
-12
| --- www/server/whyuseaserver.wiki | ||
| +++ www/server/whyuseaserver.wiki | ||
| @@ -12,11 +12,12 @@ | ||
| 12 | 12 | <h2>But, a Server Can Be Useful</h2> |
| 13 | 13 | |
| 14 | 14 | Fossil does not require a server, but a server can be very useful. |
| 15 | 15 | Here are a few reasons to set up a Fossil server for your project: |
| 16 | 16 | |
| 17 | - 1. <b>A server works as a complete project website.</b><p> | |
| 17 | + 1. <b>A server works as a complete project website.</b> | |
| 18 | + | |
| 18 | 19 | Fossil does more than just version control. It also supports |
| 19 | 20 | [../tickets.wiki|trouble-tickets], |
| 20 | 21 | [../wikitheory.wiki|wiki], |
| 21 | 22 | a [../chat.md|developer chat room], and a [../forum.wiki|forum]. |
| 22 | 23 | The [../embeddeddoc.wiki|embedded documentation] |
| @@ -23,11 +24,12 @@ | ||
| 23 | 24 | feature provides a great mechanism for providing project documentation. |
| 24 | 25 | The [../unvers.wiki|unversioned files] feature is a convenient way |
| 25 | 26 | to host builds and downloads on the project website. |
| 26 | 27 | |
| 27 | 28 | 2. <b>A server gives developers a common point of rendezvous for |
| 28 | - syncing their work.</b><p> | |
| 29 | + syncing their work.</b> | |
| 30 | + | |
| 29 | 31 | It is possible for developers to synchronize peer-to-peer but |
| 30 | 32 | that requires the developers coordinate the sync, which in turn |
| 31 | 33 | requires that the developers both want to sync at the same moment. |
| 32 | 34 | A server alleviates this time dependency by allowing each developer |
| 33 | 35 | to sync whenever it is convenient. For example, a developer may |
| @@ -34,52 +36,60 @@ | ||
| 34 | 36 | choose to automatically sync |
| 35 | 37 | after each commit and before each update. Developers all stay |
| 36 | 38 | in sync with each other without having to interrupt each other |
| 37 | 39 | constantly to set up a peer-to-peer sync. |
| 38 | 40 | |
| 39 | - 3. <b>A server provides project leaders with up-to-date status.</b><p> | |
| 41 | + 3. <b>A server provides project leaders with up-to-date status.</b> | |
| 42 | + | |
| 40 | 43 | Project coordinators and BDFLs can click on a link or two at the |
| 41 | 44 | central Fossil server for a project and quickly tell what is |
| 42 | 45 | going on. They can do this from anywhere — even from their phones |
| 43 | 46 | — without needing to actually sync to the device they are using. |
| 44 | 47 | |
| 45 | - 4. <b>A server provides automatic off-site backups.</b><p> | |
| 48 | + 4. <b>A server provides automatic off-site backups.</b> | |
| 49 | + | |
| 46 | 50 | A Fossil server is an automatic remote backup for all the work |
| 47 | 51 | going into a project. ([../backup.md | Within limits].) |
| 48 | 52 | You can even set up multiple servers at |
| 49 | 53 | multiple sites with automatic synchronization between them for |
| 50 | 54 | added redundancy. Such a setup means that no work is lost due |
| 51 | 55 | to a single machine failure. |
| 52 | 56 | |
| 53 | 57 | 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html |
| 54 | - | SQLite corruption risk mitigation] to a single point.</b><p> | |
| 58 | + | SQLite corruption risk mitigation] to a single point.</b> | |
| 59 | + | |
| 55 | 60 | The concerns in section 1 of that document assume you have direct |
| 56 | 61 | access to the central DB files, which isn't the case when the |
| 57 | - server is remote and secure against tampering.<p> | |
| 62 | + server is remote and secure against tampering. | |
| 63 | + | |
| 58 | 64 | Section 2 is about file locking, which concerns disappear when Fossil's |
| 59 | 65 | on the other side of an HTTP boundary and your server is set up |
| 60 | - properly.<p> | |
| 66 | + properly. | |
| 67 | + | |
| 61 | 68 | Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations, |
| 62 | 69 | but setting up a server lets you address the risks |
| 63 | 70 | in a single place. Once a given commit is |
| 64 | 71 | sync'd to the server, you can be reasonably sure any client-side |
| 65 | 72 | corruption can be fixed with a fresh clone. Ultimately, this |
| 66 | 73 | is an argument for off-machine backups, which returns us to reason |
| 67 | - #4 above.<p> | |
| 74 | + #4 above. | |
| 75 | + | |
| 68 | 76 | Sections 3.2 and the entirety of section 7 are no concern with |
| 69 | 77 | Fossil at all, since it's primarily written by the creator and |
| 70 | 78 | primary maintainer of SQLite, so you can be certain Fossil doesn't |
| 71 | - actively pursue coding strategies known to risk database corruption.<p> | |
| 72 | - <p>For another take on this topic, see the article | |
| 79 | + actively pursue coding strategies known to risk database corruption. | |
| 80 | + | |
| 81 | + For another take on this topic, see the article | |
| 73 | 82 | "[https://sqlite.org/useovernet.html | SQLite Over a Network, |
| 74 | 83 | Caveats and Considerations]". Fossil runs in rollback mode by |
| 75 | 84 | default per recommendation #3 at the end of that article, and a |
| 76 | 85 | Fossil server operates as a network proxy for the underlying |
| 77 | 86 | SQLite repository DB per recommendation #2. This <i>may</i> permit |
| 78 | 87 | you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>) |
| 79 | - depending on the underlying storage used by the server itself.</p> | |
| 88 | + depending on the underlying storage used by the server itself. | |
| 80 | 89 | |
| 81 | - 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b><p> | |
| 90 | + 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b> | |
| 91 | + | |
| 82 | 92 | The role-based access control (RBAC) system in Fossil only works |
| 83 | 93 | when the remote system is on the other side of an HTTP barrier. |
| 84 | 94 | ([../caps/#webonly | Details].) If you want its benefits, you need |
| 85 | 95 | a Fossil server setup of some kind. |
| 86 | 96 |
| --- www/server/whyuseaserver.wiki | |
| +++ www/server/whyuseaserver.wiki | |
| @@ -12,11 +12,12 @@ | |
| 12 | <h2>But, a Server Can Be Useful</h2> |
| 13 | |
| 14 | Fossil does not require a server, but a server can be very useful. |
| 15 | Here are a few reasons to set up a Fossil server for your project: |
| 16 | |
| 17 | 1. <b>A server works as a complete project website.</b><p> |
| 18 | Fossil does more than just version control. It also supports |
| 19 | [../tickets.wiki|trouble-tickets], |
| 20 | [../wikitheory.wiki|wiki], |
| 21 | a [../chat.md|developer chat room], and a [../forum.wiki|forum]. |
| 22 | The [../embeddeddoc.wiki|embedded documentation] |
| @@ -23,11 +24,12 @@ | |
| 23 | feature provides a great mechanism for providing project documentation. |
| 24 | The [../unvers.wiki|unversioned files] feature is a convenient way |
| 25 | to host builds and downloads on the project website. |
| 26 | |
| 27 | 2. <b>A server gives developers a common point of rendezvous for |
| 28 | syncing their work.</b><p> |
| 29 | It is possible for developers to synchronize peer-to-peer but |
| 30 | that requires the developers coordinate the sync, which in turn |
| 31 | requires that the developers both want to sync at the same moment. |
| 32 | A server alleviates this time dependency by allowing each developer |
| 33 | to sync whenever it is convenient. For example, a developer may |
| @@ -34,52 +36,60 @@ | |
| 34 | choose to automatically sync |
| 35 | after each commit and before each update. Developers all stay |
| 36 | in sync with each other without having to interrupt each other |
| 37 | constantly to set up a peer-to-peer sync. |
| 38 | |
| 39 | 3. <b>A server provides project leaders with up-to-date status.</b><p> |
| 40 | Project coordinators and BDFLs can click on a link or two at the |
| 41 | central Fossil server for a project and quickly tell what is |
| 42 | going on. They can do this from anywhere — even from their phones |
| 43 | — without needing to actually sync to the device they are using. |
| 44 | |
| 45 | 4. <b>A server provides automatic off-site backups.</b><p> |
| 46 | A Fossil server is an automatic remote backup for all the work |
| 47 | going into a project. ([../backup.md | Within limits].) |
| 48 | You can even set up multiple servers at |
| 49 | multiple sites with automatic synchronization between them for |
| 50 | added redundancy. Such a setup means that no work is lost due |
| 51 | to a single machine failure. |
| 52 | |
| 53 | 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html |
| 54 | | SQLite corruption risk mitigation] to a single point.</b><p> |
| 55 | The concerns in section 1 of that document assume you have direct |
| 56 | access to the central DB files, which isn't the case when the |
| 57 | server is remote and secure against tampering.<p> |
| 58 | Section 2 is about file locking, which concerns disappear when Fossil's |
| 59 | on the other side of an HTTP boundary and your server is set up |
| 60 | properly.<p> |
| 61 | Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations, |
| 62 | but setting up a server lets you address the risks |
| 63 | in a single place. Once a given commit is |
| 64 | sync'd to the server, you can be reasonably sure any client-side |
| 65 | corruption can be fixed with a fresh clone. Ultimately, this |
| 66 | is an argument for off-machine backups, which returns us to reason |
| 67 | #4 above.<p> |
| 68 | Sections 3.2 and the entirety of section 7 are no concern with |
| 69 | Fossil at all, since it's primarily written by the creator and |
| 70 | primary maintainer of SQLite, so you can be certain Fossil doesn't |
| 71 | actively pursue coding strategies known to risk database corruption.<p> |
| 72 | <p>For another take on this topic, see the article |
| 73 | "[https://sqlite.org/useovernet.html | SQLite Over a Network, |
| 74 | Caveats and Considerations]". Fossil runs in rollback mode by |
| 75 | default per recommendation #3 at the end of that article, and a |
| 76 | Fossil server operates as a network proxy for the underlying |
| 77 | SQLite repository DB per recommendation #2. This <i>may</i> permit |
| 78 | you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>) |
| 79 | depending on the underlying storage used by the server itself.</p> |
| 80 | |
| 81 | 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b><p> |
| 82 | The role-based access control (RBAC) system in Fossil only works |
| 83 | when the remote system is on the other side of an HTTP barrier. |
| 84 | ([../caps/#webonly | Details].) If you want its benefits, you need |
| 85 | a Fossil server setup of some kind. |
| 86 |
| --- www/server/whyuseaserver.wiki | |
| +++ www/server/whyuseaserver.wiki | |
| @@ -12,11 +12,12 @@ | |
| 12 | <h2>But, a Server Can Be Useful</h2> |
| 13 | |
| 14 | Fossil does not require a server, but a server can be very useful. |
| 15 | Here are a few reasons to set up a Fossil server for your project: |
| 16 | |
| 17 | 1. <b>A server works as a complete project website.</b> |
| 18 | |
| 19 | Fossil does more than just version control. It also supports |
| 20 | [../tickets.wiki|trouble-tickets], |
| 21 | [../wikitheory.wiki|wiki], |
| 22 | a [../chat.md|developer chat room], and a [../forum.wiki|forum]. |
| 23 | The [../embeddeddoc.wiki|embedded documentation] |
| @@ -23,11 +24,12 @@ | |
| 24 | feature provides a great mechanism for providing project documentation. |
| 25 | The [../unvers.wiki|unversioned files] feature is a convenient way |
| 26 | to host builds and downloads on the project website. |
| 27 | |
| 28 | 2. <b>A server gives developers a common point of rendezvous for |
| 29 | syncing their work.</b> |
| 30 | |
| 31 | It is possible for developers to synchronize peer-to-peer but |
| 32 | that requires the developers coordinate the sync, which in turn |
| 33 | requires that the developers both want to sync at the same moment. |
| 34 | A server alleviates this time dependency by allowing each developer |
| 35 | to sync whenever it is convenient. For example, a developer may |
| @@ -34,52 +36,60 @@ | |
| 36 | choose to automatically sync |
| 37 | after each commit and before each update. Developers all stay |
| 38 | in sync with each other without having to interrupt each other |
| 39 | constantly to set up a peer-to-peer sync. |
| 40 | |
| 41 | 3. <b>A server provides project leaders with up-to-date status.</b> |
| 42 | |
| 43 | Project coordinators and BDFLs can click on a link or two at the |
| 44 | central Fossil server for a project and quickly tell what is |
| 45 | going on. They can do this from anywhere — even from their phones |
| 46 | — without needing to actually sync to the device they are using. |
| 47 | |
| 48 | 4. <b>A server provides automatic off-site backups.</b> |
| 49 | |
| 50 | A Fossil server is an automatic remote backup for all the work |
| 51 | going into a project. ([../backup.md | Within limits].) |
| 52 | You can even set up multiple servers at |
| 53 | multiple sites with automatic synchronization between them for |
| 54 | added redundancy. Such a setup means that no work is lost due |
| 55 | to a single machine failure. |
| 56 | |
| 57 | 5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html |
| 58 | | SQLite corruption risk mitigation] to a single point.</b> |
| 59 | |
| 60 | The concerns in section 1 of that document assume you have direct |
| 61 | access to the central DB files, which isn't the case when the |
| 62 | server is remote and secure against tampering. |
| 63 | |
| 64 | Section 2 is about file locking, which concerns disappear when Fossil's |
| 65 | on the other side of an HTTP boundary and your server is set up |
| 66 | properly. |
| 67 | |
| 68 | Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations, |
| 69 | but setting up a server lets you address the risks |
| 70 | in a single place. Once a given commit is |
| 71 | sync'd to the server, you can be reasonably sure any client-side |
| 72 | corruption can be fixed with a fresh clone. Ultimately, this |
| 73 | is an argument for off-machine backups, which returns us to reason |
| 74 | #4 above. |
| 75 | |
| 76 | Sections 3.2 and the entirety of section 7 are no concern with |
| 77 | Fossil at all, since it's primarily written by the creator and |
| 78 | primary maintainer of SQLite, so you can be certain Fossil doesn't |
| 79 | actively pursue coding strategies known to risk database corruption. |
| 80 | |
| 81 | For another take on this topic, see the article |
| 82 | "[https://sqlite.org/useovernet.html | SQLite Over a Network, |
| 83 | Caveats and Considerations]". Fossil runs in rollback mode by |
| 84 | default per recommendation #3 at the end of that article, and a |
| 85 | Fossil server operates as a network proxy for the underlying |
| 86 | SQLite repository DB per recommendation #2. This <i>may</i> permit |
| 87 | you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>) |
| 88 | depending on the underlying storage used by the server itself. |
| 89 | |
| 90 | 6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b> |
| 91 | |
| 92 | The role-based access control (RBAC) system in Fossil only works |
| 93 | when the remote system is on the other side of an HTTP barrier. |
| 94 | ([../caps/#webonly | Details].) If you want its benefits, you need |
| 95 | a Fossil server setup of some kind. |
| 96 |
+2
-2
| --- www/settings.wiki | ||
| +++ www/settings.wiki | ||
| @@ -8,11 +8,11 @@ | ||
| 8 | 8 | |
| 9 | 9 | For a list of all settings, view the Settings page, or type |
| 10 | 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | 11 | |
| 12 | 12 | |
| 13 | -<h3>Repository settings</h3> | |
| 13 | +<h3 id="repo">Repository settings</h3> | |
| 14 | 14 | |
| 15 | 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | 16 | a subset of settings are copied to your local repository. |
| 17 | 17 | |
| 18 | 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | will be used for all repositories cloned to your machine, unless |
| 25 | 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | 27 | command. |
| 28 | 28 | |
| 29 | -<h3>"Versionable" settings</h3> | |
| 29 | +<h3 id="versionable">"Versionable" settings</h3> | |
| 30 | 30 | |
| 31 | 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | 34 | a repository on the web. |
| 35 | 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -8,11 +8,11 @@ | |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h3>Repository settings</h3> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h3>"Versionable" settings</h3> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -8,11 +8,11 @@ | |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h3 id="repo">Repository settings</h3> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h3 id="versionable">"Versionable" settings</h3> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |
+49
-50
| --- www/shunning.wiki | ||
| +++ www/shunning.wiki | ||
| @@ -37,60 +37,59 @@ | ||
| 37 | 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | 38 | methods of permanently deleting content from a Fossil repos, let's give |
| 39 | 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | 40 | fossil record: |
| 41 | 41 | |
| 42 | -<ul> | |
| 43 | - <li><p>When a forum post or wiki article is "deleted," what actually | |
| 44 | - happens is that a new empty version is added to the Fossil repository. | |
| 45 | - The web interface interprets this | |
| 46 | - as "deleted," but the prior version remains available if you go | |
| 47 | - digging for it.</p></li> | |
| 48 | - | |
| 49 | - <li><p>When you close a ticket, it's marked in a way that causes it | |
| 50 | - to not show up in the normal ticket reports. You usually want to | |
| 51 | - give it a Resolution such as "Rejected" when this happens, plus | |
| 52 | - possibly a comment explaining why you're closing it. This is all new | |
| 53 | - information added to the ticket, not deletion.</p></li> | |
| 54 | - | |
| 55 | - <li><p>When you <tt>fossil rm</tt> a file, a new manifest is | |
| 56 | - checked into the repository with the same file list as for the prior | |
| 57 | - version minus the "removed" file. The file is still present in the | |
| 58 | - repository; it just isn't part of that version forward on that | |
| 59 | - branch.</p></li> | |
| 60 | - | |
| 61 | - <li><p>If you make a bad check-in, you can shunt it off to the side | |
| 62 | - by amending it to put it on a different branch, then continuing | |
| 63 | - development on the prior branch: | |
| 64 | - <p> | |
| 65 | - <tt>$ fossil amend abcd1234 --branch BOGUS --hide<br> | |
| 66 | - $ fossil up trunk</tt> | |
| 67 | - <p> | |
| 68 | - The first command moves check-in ID <tt>abcd1234</tt> (and any | |
| 69 | - subsequent check-ins on that branch!) to a branch called | |
| 70 | - <tt>BOGUS</tt>, then hides it so it doesn't show up on the | |
| 71 | - timeline. You can call this branch anything you like, and you can | |
| 72 | - re-use the same name as many times as you like. No content is | |
| 73 | - actually deleted: it's just shunted off to the side and hidden away. | |
| 74 | - You might find it easier to do this from the Fossil web UI in | |
| 75 | - the "edit" function for a check-in. | |
| 76 | - <p> | |
| 77 | - The second command returns to the last good check-in on that branch | |
| 78 | - so you can continue work from that point.</p></li> | |
| 79 | - | |
| 80 | - <li><p>When the check-in you want to remove is followed by good | |
| 81 | - check-ins on the same branch, you can't use the previous method, | |
| 82 | - because it will move the good check-ins, too. The solution is: | |
| 83 | - <p> | |
| 84 | - <tt>$ fossil merge --backout abcd1234</tt> | |
| 85 | - <p>That creates a diff in the check-out directory that backs out the | |
| 86 | - bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts, | |
| 87 | - build, test, etc., then check the reverting change into the | |
| 88 | - repository. Again, nothing is actually deleted; you're just adding | |
| 89 | - more information to the repository which corrects a prior | |
| 90 | - check-in.</p></li> | |
| 91 | -</ul> | |
| 42 | + * When a forum post or wiki article is "deleted," what actually | |
| 43 | + happens is that a new empty version is added to the Fossil repository. | |
| 44 | + The web interface interprets this | |
| 45 | + as "deleted," but the prior version remains available if you go | |
| 46 | + digging for it. | |
| 47 | + | |
| 48 | + * When you close a ticket, it's marked in a way that causes it | |
| 49 | + to not show up in the normal ticket reports. You usually want to | |
| 50 | + give it a Resolution such as "Rejected" when this happens, plus | |
| 51 | + possibly a comment explaining why you're closing it. This is all new | |
| 52 | + information added to the ticket, not deletion. | |
| 53 | + | |
| 54 | + * When you <tt>fossil rm</tt> a file, a new manifest is | |
| 55 | + checked into the repository with the same file list as for the prior | |
| 56 | + version minus the "removed" file. The file is still present in the | |
| 57 | + repository; it just isn't part of that version forward on that | |
| 58 | + branch. | |
| 59 | + | |
| 60 | + * If you make a bad check-in, you can shunt it off to the side | |
| 61 | + by amending it to put it on a different branch, then continuing | |
| 62 | + development on the prior branch: | |
| 63 | + <br><br> | |
| 64 | + <code>$ fossil amend abcd1234 --branch BOGUS --hide<br> | |
| 65 | + $ fossil up trunk</code> | |
| 66 | + <br><br> | |
| 67 | + The first command moves check-in ID <tt>abcd1234</tt> (and any | |
| 68 | + subsequent check-ins on that branch!) to a branch called | |
| 69 | + <tt>BOGUS</tt>, then hides it so it doesn't show up on the | |
| 70 | + timeline. You can call this branch anything you like, and you can | |
| 71 | + re-use the same name as many times as you like. No content is | |
| 72 | + actually deleted: it's just shunted off to the side and hidden away. | |
| 73 | + You might find it easier to do this from the Fossil web UI in | |
| 74 | + the "edit" function for a check-in. | |
| 75 | + <br><br> | |
| 76 | + The second command returns to the last good check-in on that branch | |
| 77 | + so you can continue work from that point. | |
| 78 | + | |
| 79 | + * When the check-in you want to remove is followed by good | |
| 80 | + check-ins on the same branch, you can't use the previous method, | |
| 81 | + because it will move the good check-ins, too. The solution is: | |
| 82 | + <br><br> | |
| 83 | + <tt>$ fossil merge --backout abcd1234</tt> | |
| 84 | + <br><br> | |
| 85 | + That creates a diff in the check-out directory that backs out the | |
| 86 | + bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts, | |
| 87 | + build, test, etc., then check the reverting change into the | |
| 88 | + repository. Again, nothing is actually deleted; you're just adding | |
| 89 | + more information to the repository which corrects a prior | |
| 90 | + check-in. | |
| 92 | 91 | |
| 93 | 92 | <h2>Exception: Non-versioned Content</h2> |
| 94 | 93 | |
| 95 | 94 | It is normal and expected to delete data which is not versioned, such as |
| 96 | 95 | usernames and passwords in the user table. The [/help/scrub|fossil scrub] |
| 97 | 96 |
| --- www/shunning.wiki | |
| +++ www/shunning.wiki | |
| @@ -37,60 +37,59 @@ | |
| 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | methods of permanently deleting content from a Fossil repos, let's give |
| 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | fossil record: |
| 41 | |
| 42 | <ul> |
| 43 | <li><p>When a forum post or wiki article is "deleted," what actually |
| 44 | happens is that a new empty version is added to the Fossil repository. |
| 45 | The web interface interprets this |
| 46 | as "deleted," but the prior version remains available if you go |
| 47 | digging for it.</p></li> |
| 48 | |
| 49 | <li><p>When you close a ticket, it's marked in a way that causes it |
| 50 | to not show up in the normal ticket reports. You usually want to |
| 51 | give it a Resolution such as "Rejected" when this happens, plus |
| 52 | possibly a comment explaining why you're closing it. This is all new |
| 53 | information added to the ticket, not deletion.</p></li> |
| 54 | |
| 55 | <li><p>When you <tt>fossil rm</tt> a file, a new manifest is |
| 56 | checked into the repository with the same file list as for the prior |
| 57 | version minus the "removed" file. The file is still present in the |
| 58 | repository; it just isn't part of that version forward on that |
| 59 | branch.</p></li> |
| 60 | |
| 61 | <li><p>If you make a bad check-in, you can shunt it off to the side |
| 62 | by amending it to put it on a different branch, then continuing |
| 63 | development on the prior branch: |
| 64 | <p> |
| 65 | <tt>$ fossil amend abcd1234 --branch BOGUS --hide<br> |
| 66 | $ fossil up trunk</tt> |
| 67 | <p> |
| 68 | The first command moves check-in ID <tt>abcd1234</tt> (and any |
| 69 | subsequent check-ins on that branch!) to a branch called |
| 70 | <tt>BOGUS</tt>, then hides it so it doesn't show up on the |
| 71 | timeline. You can call this branch anything you like, and you can |
| 72 | re-use the same name as many times as you like. No content is |
| 73 | actually deleted: it's just shunted off to the side and hidden away. |
| 74 | You might find it easier to do this from the Fossil web UI in |
| 75 | the "edit" function for a check-in. |
| 76 | <p> |
| 77 | The second command returns to the last good check-in on that branch |
| 78 | so you can continue work from that point.</p></li> |
| 79 | |
| 80 | <li><p>When the check-in you want to remove is followed by good |
| 81 | check-ins on the same branch, you can't use the previous method, |
| 82 | because it will move the good check-ins, too. The solution is: |
| 83 | <p> |
| 84 | <tt>$ fossil merge --backout abcd1234</tt> |
| 85 | <p>That creates a diff in the check-out directory that backs out the |
| 86 | bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts, |
| 87 | build, test, etc., then check the reverting change into the |
| 88 | repository. Again, nothing is actually deleted; you're just adding |
| 89 | more information to the repository which corrects a prior |
| 90 | check-in.</p></li> |
| 91 | </ul> |
| 92 | |
| 93 | <h2>Exception: Non-versioned Content</h2> |
| 94 | |
| 95 | It is normal and expected to delete data which is not versioned, such as |
| 96 | usernames and passwords in the user table. The [/help/scrub|fossil scrub] |
| 97 |
| --- www/shunning.wiki | |
| +++ www/shunning.wiki | |
| @@ -37,60 +37,59 @@ | |
| 37 | Fossil to insert bad control artifacts. Therefore, before we get to |
| 38 | methods of permanently deleting content from a Fossil repos, let's give |
| 39 | some alternatives that usually suffice, which don't damage the project's |
| 40 | fossil record: |
| 41 | |
| 42 | * When a forum post or wiki article is "deleted," what actually |
| 43 | happens is that a new empty version is added to the Fossil repository. |
| 44 | The web interface interprets this |
| 45 | as "deleted," but the prior version remains available if you go |
| 46 | digging for it. |
| 47 | |
| 48 | * When you close a ticket, it's marked in a way that causes it |
| 49 | to not show up in the normal ticket reports. You usually want to |
| 50 | give it a Resolution such as "Rejected" when this happens, plus |
| 51 | possibly a comment explaining why you're closing it. This is all new |
| 52 | information added to the ticket, not deletion. |
| 53 | |
| 54 | * When you <tt>fossil rm</tt> a file, a new manifest is |
| 55 | checked into the repository with the same file list as for the prior |
| 56 | version minus the "removed" file. The file is still present in the |
| 57 | repository; it just isn't part of that version forward on that |
| 58 | branch. |
| 59 | |
| 60 | * If you make a bad check-in, you can shunt it off to the side |
| 61 | by amending it to put it on a different branch, then continuing |
| 62 | development on the prior branch: |
| 63 | <br><br> |
| 64 | <code>$ fossil amend abcd1234 --branch BOGUS --hide<br> |
| 65 | $ fossil up trunk</code> |
| 66 | <br><br> |
| 67 | The first command moves check-in ID <tt>abcd1234</tt> (and any |
| 68 | subsequent check-ins on that branch!) to a branch called |
| 69 | <tt>BOGUS</tt>, then hides it so it doesn't show up on the |
| 70 | timeline. You can call this branch anything you like, and you can |
| 71 | re-use the same name as many times as you like. No content is |
| 72 | actually deleted: it's just shunted off to the side and hidden away. |
| 73 | You might find it easier to do this from the Fossil web UI in |
| 74 | the "edit" function for a check-in. |
| 75 | <br><br> |
| 76 | The second command returns to the last good check-in on that branch |
| 77 | so you can continue work from that point. |
| 78 | |
| 79 | * When the check-in you want to remove is followed by good |
| 80 | check-ins on the same branch, you can't use the previous method, |
| 81 | because it will move the good check-ins, too. The solution is: |
| 82 | <br><br> |
| 83 | <tt>$ fossil merge --backout abcd1234</tt> |
| 84 | <br><br> |
| 85 | That creates a diff in the check-out directory that backs out the |
| 86 | bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts, |
| 87 | build, test, etc., then check the reverting change into the |
| 88 | repository. Again, nothing is actually deleted; you're just adding |
| 89 | more information to the repository which corrects a prior |
| 90 | check-in. |
| 91 | |
| 92 | <h2>Exception: Non-versioned Content</h2> |
| 93 | |
| 94 | It is normal and expected to delete data which is not versioned, such as |
| 95 | usernames and passwords in the user table. The [/help/scrub|fossil scrub] |
| 96 |
+14
-11
| --- www/ssl.wiki | ||
| +++ www/ssl.wiki | ||
| @@ -140,15 +140,16 @@ | ||
| 140 | 140 | </pre> |
| 141 | 141 | |
| 142 | 142 | Fossil relies on the OpenSSL library to have some way to check a trusted |
| 143 | 143 | list of CA signing keys. There are two common ways this fails: |
| 144 | 144 | |
| 145 | - # <p>The OpenSSL library Fossil is linked to doesn't have a CA | |
| 145 | + # The OpenSSL library Fossil is linked to doesn't have a CA | |
| 146 | 146 | signing key set at all, so that it initially trusts no certificates |
| 147 | - at all.</p> | |
| 148 | - # <p>The OpenSSL library does have a CA cert set, but your Fossil server's | |
| 149 | - TLS certificate was signed by a CA that isn't in that set.</p> | |
| 147 | + at all. | |
| 148 | + | |
| 149 | + # The OpenSSL library does have a CA cert set, but your Fossil server's | |
| 150 | + TLS certificate was signed by a CA that isn't in that set. | |
| 150 | 151 | |
| 151 | 152 | A common reason to fall into the second trap is that you're using |
| 152 | 153 | certificates signed by a local private CA, as often happens in large |
| 153 | 154 | enterprises. You can solve this sort of problem by getting your local |
| 154 | 155 | CA's signing certificate in PEM format and pointing OpenSSL at it: |
| @@ -272,26 +273,28 @@ | ||
| 272 | 273 | it issues a redirect...'round and 'round it goes until the web browser |
| 273 | 274 | detects it's in a redirect loop and gives up. This problem prevents you |
| 274 | 275 | from getting back into the Admin UI to fix it, but there are several |
| 275 | 276 | ways to fix it: |
| 276 | 277 | |
| 277 | - # <p><b>Reset via CLI.</b> You can turn the setting back off from the | |
| 278 | + # <b>Reset via CLI.</b> You can turn the setting back off from the | |
| 278 | 279 | CLI with the command "<tt>fossil -R /path/to/repo.fossil set |
| 279 | - redirect-to-https 0</tt>". (Currently doesn't work.)</p> | |
| 280 | - # <p><b>Backup first.</b> This setting is stored in the Fossil | |
| 280 | + redirect-to-https 0</tt>". (Currently doesn't work.) | |
| 281 | + | |
| 282 | + # <b>Backup first.</b> This setting is stored in the Fossil | |
| 281 | 283 | repository, so if you make a backup first <i>on the server</i>, you |
| 282 | 284 | can restore the repo file if enabling this feature creates a |
| 283 | - redirect loop.</p> | |
| 284 | - # <p><b>Download, fix, and restore.</b> You can copy the remote | |
| 285 | + redirect loop. | |
| 286 | + | |
| 287 | + # <b>Download, fix, and restore.</b> You can copy the remote | |
| 285 | 288 | repository file down to a local machine, use <tt>fossil ui</tt> to |
| 286 | 289 | fix the setting, and then upload it to the repository server |
| 287 | - again.</p> | |
| 290 | + again. | |
| 288 | 291 | |
| 289 | 292 | It's best to enforce TLS-only access at the front-end proxy level |
| 290 | 293 | anyway. It not only avoids the problem entirely, it can be significantly |
| 291 | 294 | more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way |
| 292 | -to achieve this.</p> | |
| 295 | +to achieve this. | |
| 293 | 296 | |
| 294 | 297 | |
| 295 | 298 | <h2>Terminology Note</h2> |
| 296 | 299 | |
| 297 | 300 | This document is called <tt>ssl.wiki</tt> for historical reasons. The |
| 298 | 301 |
| --- www/ssl.wiki | |
| +++ www/ssl.wiki | |
| @@ -140,15 +140,16 @@ | |
| 140 | </pre> |
| 141 | |
| 142 | Fossil relies on the OpenSSL library to have some way to check a trusted |
| 143 | list of CA signing keys. There are two common ways this fails: |
| 144 | |
| 145 | # <p>The OpenSSL library Fossil is linked to doesn't have a CA |
| 146 | signing key set at all, so that it initially trusts no certificates |
| 147 | at all.</p> |
| 148 | # <p>The OpenSSL library does have a CA cert set, but your Fossil server's |
| 149 | TLS certificate was signed by a CA that isn't in that set.</p> |
| 150 | |
| 151 | A common reason to fall into the second trap is that you're using |
| 152 | certificates signed by a local private CA, as often happens in large |
| 153 | enterprises. You can solve this sort of problem by getting your local |
| 154 | CA's signing certificate in PEM format and pointing OpenSSL at it: |
| @@ -272,26 +273,28 @@ | |
| 272 | it issues a redirect...'round and 'round it goes until the web browser |
| 273 | detects it's in a redirect loop and gives up. This problem prevents you |
| 274 | from getting back into the Admin UI to fix it, but there are several |
| 275 | ways to fix it: |
| 276 | |
| 277 | # <p><b>Reset via CLI.</b> You can turn the setting back off from the |
| 278 | CLI with the command "<tt>fossil -R /path/to/repo.fossil set |
| 279 | redirect-to-https 0</tt>". (Currently doesn't work.)</p> |
| 280 | # <p><b>Backup first.</b> This setting is stored in the Fossil |
| 281 | repository, so if you make a backup first <i>on the server</i>, you |
| 282 | can restore the repo file if enabling this feature creates a |
| 283 | redirect loop.</p> |
| 284 | # <p><b>Download, fix, and restore.</b> You can copy the remote |
| 285 | repository file down to a local machine, use <tt>fossil ui</tt> to |
| 286 | fix the setting, and then upload it to the repository server |
| 287 | again.</p> |
| 288 | |
| 289 | It's best to enforce TLS-only access at the front-end proxy level |
| 290 | anyway. It not only avoids the problem entirely, it can be significantly |
| 291 | more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way |
| 292 | to achieve this.</p> |
| 293 | |
| 294 | |
| 295 | <h2>Terminology Note</h2> |
| 296 | |
| 297 | This document is called <tt>ssl.wiki</tt> for historical reasons. The |
| 298 |
| --- www/ssl.wiki | |
| +++ www/ssl.wiki | |
| @@ -140,15 +140,16 @@ | |
| 140 | </pre> |
| 141 | |
| 142 | Fossil relies on the OpenSSL library to have some way to check a trusted |
| 143 | list of CA signing keys. There are two common ways this fails: |
| 144 | |
| 145 | # The OpenSSL library Fossil is linked to doesn't have a CA |
| 146 | signing key set at all, so that it initially trusts no certificates |
| 147 | at all. |
| 148 | |
| 149 | # The OpenSSL library does have a CA cert set, but your Fossil server's |
| 150 | TLS certificate was signed by a CA that isn't in that set. |
| 151 | |
| 152 | A common reason to fall into the second trap is that you're using |
| 153 | certificates signed by a local private CA, as often happens in large |
| 154 | enterprises. You can solve this sort of problem by getting your local |
| 155 | CA's signing certificate in PEM format and pointing OpenSSL at it: |
| @@ -272,26 +273,28 @@ | |
| 273 | it issues a redirect...'round and 'round it goes until the web browser |
| 274 | detects it's in a redirect loop and gives up. This problem prevents you |
| 275 | from getting back into the Admin UI to fix it, but there are several |
| 276 | ways to fix it: |
| 277 | |
| 278 | # <b>Reset via CLI.</b> You can turn the setting back off from the |
| 279 | CLI with the command "<tt>fossil -R /path/to/repo.fossil set |
| 280 | redirect-to-https 0</tt>". (Currently doesn't work.) |
| 281 | |
| 282 | # <b>Backup first.</b> This setting is stored in the Fossil |
| 283 | repository, so if you make a backup first <i>on the server</i>, you |
| 284 | can restore the repo file if enabling this feature creates a |
| 285 | redirect loop. |
| 286 | |
| 287 | # <b>Download, fix, and restore.</b> You can copy the remote |
| 288 | repository file down to a local machine, use <tt>fossil ui</tt> to |
| 289 | fix the setting, and then upload it to the repository server |
| 290 | again. |
| 291 | |
| 292 | It's best to enforce TLS-only access at the front-end proxy level |
| 293 | anyway. It not only avoids the problem entirely, it can be significantly |
| 294 | more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way |
| 295 | to achieve this. |
| 296 | |
| 297 | |
| 298 | <h2>Terminology Note</h2> |
| 299 | |
| 300 | This document is called <tt>ssl.wiki</tt> for historical reasons. The |
| 301 |
+191
-200
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -1,13 +1,13 @@ | ||
| 1 | 1 | <title>The Fossil Sync Protocol</title> |
| 2 | 2 | |
| 3 | -<p>This document describes the wire protocol used to synchronize | |
| 4 | -content between two Fossil repositories.</p> | |
| 3 | +This document describes the wire protocol used to synchronize | |
| 4 | +content between two Fossil repositories. | |
| 5 | 5 | |
| 6 | 6 | <h2>1.0 Overview</h2> |
| 7 | 7 | |
| 8 | -<p>The global state of a fossil repository consists of an unordered | |
| 8 | +The global state of a fossil repository consists of an unordered | |
| 9 | 9 | collection of artifacts. Each artifact is identified by a cryptographic |
| 10 | 10 | hash of its content, expressed as a lower-case hexadecimal string. |
| 11 | 11 | Synchronization is the process of sharing artifacts between |
| 12 | 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | 13 | artifacts are unordered, the order in which artifacts are received |
| @@ -17,13 +17,13 @@ | ||
| 17 | 17 | of hashes for available artifacts, then sharing the content of artifacts |
| 18 | 18 | whose names are missing from one side or the other of the connection. |
| 19 | 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | 21 | employed that usually reduce the number of hashes that need to be |
| 22 | -shared to a few hundred.</p> | |
| 22 | +shared to a few hundred. | |
| 23 | 23 | |
| 24 | -<p>Each repository also has local state. The local state determines | |
| 24 | +Each repository also has local state. The local state determines | |
| 25 | 25 | the web-page formatting preferences, authorized users, ticket formats, |
| 26 | 26 | and similar information that varies from one repository to another. |
| 27 | 27 | The local state is not usually transferred during a sync. Except, |
| 28 | 28 | some local state is transferred during a [/help?cmd=clone|clone] |
| 29 | 29 | in order to initialize the local state of the new repository. Also, |
| @@ -32,67 +32,67 @@ | ||
| 32 | 32 | [/help?cmd=configuration|config pull] |
| 33 | 33 | commands. |
| 34 | 34 | |
| 35 | 35 | <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> |
| 36 | 36 | |
| 37 | -<p>The "bag of artifacts" data model used by Fossil is apparently an | |
| 37 | +The "bag of artifacts" data model used by Fossil is apparently an | |
| 38 | 38 | implementation of a particular |
| 39 | 39 | [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free |
| 40 | 40 | Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The |
| 41 | 41 | academic literature on CRDTs only began to appear in about 2011, and |
| 42 | 42 | Fossil predates that research by at least 4 years. But it is nice to |
| 43 | 43 | know that theorists have now proven that the underlying data model of |
| 44 | 44 | Fossil can provide strongly-consistent replicas using only |
| 45 | 45 | peer-to-peer communication and without any kind of central |
| 46 | -authority.</p> | |
| 46 | +authority. | |
| 47 | 47 | |
| 48 | -<p>If you are already familiar with CRDTs and were wondering if Fossil | |
| 49 | -used them, the answer is "yes". We just don't call them by that name.</p> | |
| 48 | +If you are already familiar with CRDTs and were wondering if Fossil | |
| 49 | +used them, the answer is "yes". We just don't call them by that name. | |
| 50 | 50 | |
| 51 | 51 | |
| 52 | 52 | <h2>2.0 Transport</h2> |
| 53 | 53 | |
| 54 | -<p>All communication between client and server is via HTTP requests. | |
| 54 | +All communication between client and server is via HTTP requests. | |
| 55 | 55 | The server is listening for incoming HTTP requests. The client |
| 56 | 56 | issues one or more HTTP requests and receives replies for each |
| 57 | -request.</p> | |
| 57 | +request. | |
| 58 | 58 | |
| 59 | -<p>The server might be running as an independent server | |
| 59 | +The server might be running as an independent server | |
| 60 | 60 | using the <b>server</b> command, or it might be launched from |
| 61 | 61 | inetd or xinetd using the <b>http</b> command. Or the server might |
| 62 | 62 | be launched from CGI. |
| 63 | 63 | (See "[./server/|How To Configure A Fossil Server]" for details.) |
| 64 | 64 | The specifics of how the server listens |
| 65 | 65 | for incoming HTTP requests is immaterial to this protocol. |
| 66 | 66 | The important point is that the server is listening for requests and |
| 67 | -the client is the issuer of the requests.</p> | |
| 67 | +the client is the issuer of the requests. | |
| 68 | 68 | |
| 69 | -<p>A single push, pull, or sync might involve multiple HTTP requests. | |
| 69 | +A single push, pull, or sync might involve multiple HTTP requests. | |
| 70 | 70 | The client maintains state between all requests. But on the server |
| 71 | 71 | side, each request is independent. The server does not preserve |
| 72 | -any information about the client from one request to the next.</p> | |
| 72 | +any information about the client from one request to the next. | |
| 73 | 73 | |
| 74 | -<p>Note: Throughout this article, we use the terms "server" and "client" | |
| 74 | +Note: Throughout this article, we use the terms "server" and "client" | |
| 75 | 75 | to represent the listener and initiator of the interaction, respectively. |
| 76 | 76 | Nothing in this protocol requires that the server actually be a back-room |
| 77 | 77 | processor housed in a datacenter, nor does the client need to be a desktop |
| 78 | 78 | or handheld device. For the purposes of this article "client" simply means |
| 79 | 79 | the repository that initiates the conversation and "server" is the repository |
| 80 | -that responds. Nothing more.</p> | |
| 80 | +that responds. Nothing more. | |
| 81 | 81 | |
| 82 | 82 | <h4>2.0.1 HTTPS Transport</h4> |
| 83 | 83 | |
| 84 | -<p>In the current implementation of Fossil, the server only | |
| 84 | +In the current implementation of Fossil, the server only | |
| 85 | 85 | understands HTTP requests. The client can send either |
| 86 | 86 | clear-text HTTP requests or encrypted HTTPS requests. But when |
| 87 | 87 | HTTPS requests are sent, they first must be decrypted by a web server |
| 88 | 88 | or proxy before being passed to the Fossil server. This limitation |
| 89 | -may be relaxed in a future release.</p> | |
| 89 | +may be relaxed in a future release. | |
| 90 | 90 | |
| 91 | 91 | <h4>2.0.2 SSH Transport</h4> |
| 92 | 92 | |
| 93 | -<p>When doing a sync using an "ssh:..." URL, the same HTTP transport protocol | |
| 93 | +When doing a sync using an "ssh:..." URL, the same HTTP transport protocol | |
| 94 | 94 | is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh] |
| 95 | 95 | to start an instance of the [/help?cmd=test-http|fossil test-http] command |
| 96 | 96 | running on the remote machine. It then sends HTTP requests and gets back HTTP |
| 97 | 97 | replies over the SSH connection, rather than sending and receiving over an |
| 98 | 98 | internet socket. To see the specific "ssh" command that the Fossil client |
| @@ -99,11 +99,11 @@ | ||
| 99 | 99 | runs in order to set up a connection, add either of the the "--httptrace" or |
| 100 | 100 | "--sshtrace" options to the "fossil sync" command line. |
| 101 | 101 | |
| 102 | 102 | <h4>2.0.3 FILE Transport</h4> |
| 103 | 103 | |
| 104 | -<p>When doing a sync using a "file:..." URL, the same HTTP protocol is | |
| 104 | +When doing a sync using a "file:..." URL, the same HTTP protocol is | |
| 105 | 105 | still used. But instead of sending each HTTP request over a socket or |
| 106 | 106 | via SSH, the HTTP request is written into a temporary file. The client |
| 107 | 107 | then invokes the [/help?cmd=http|fossil http] command in a subprocess |
| 108 | 108 | to process the request and and generate a reply. The client then reads |
| 109 | 109 | the HTTP reply out of a temporary file on disk, and deletes the two |
| @@ -111,36 +111,36 @@ | ||
| 111 | 111 | in order to implement the "file:" transport, add the "--httptrace" |
| 112 | 112 | option to the "fossil sync" command. |
| 113 | 113 | |
| 114 | 114 | <h3>2.1 Server Identification</h3> |
| 115 | 115 | |
| 116 | -<p>The server is identified by a URL argument that accompanies the | |
| 116 | +The server is identified by a URL argument that accompanies the | |
| 117 | 117 | push, pull, or sync command on the client. (As a convenience to |
| 118 | 118 | users, the URL can be omitted on the client command and the same URL |
| 119 | 119 | from the most recent push, pull, or sync will be reused. This saves |
| 120 | 120 | typing in the common case where the client does multiple syncs to |
| 121 | -the same server.)</p> | |
| 121 | +the same server.) | |
| 122 | 122 | |
| 123 | -<p>The client modifies the URL by appending the method name "<b>/xfer</b>" | |
| 123 | +The client modifies the URL by appending the method name "<b>/xfer</b>" | |
| 124 | 124 | to the end. For example, if the URL specified on the client command |
| 125 | -line is</p> | |
| 125 | +line is | |
| 126 | 126 | |
| 127 | 127 | <blockquote> |
| 128 | 128 | https://fossil-scm.org/fossil |
| 129 | 129 | </blockquote> |
| 130 | 130 | |
| 131 | -<p>Then the URL that is really used to do the synchronization will | |
| 132 | -be:</p> | |
| 131 | +Then the URL that is really used to do the synchronization will | |
| 132 | +be: | |
| 133 | 133 | |
| 134 | 134 | <blockquote> |
| 135 | 135 | https://fossil-scm.org/fossil/xfer |
| 136 | 136 | </blockquote> |
| 137 | 137 | |
| 138 | 138 | <h3>2.2 HTTP Request Format</h3> |
| 139 | 139 | |
| 140 | -<p>The client always sends a POST request to the server. The | |
| 141 | -general format of the POST request is as follows:</p> | |
| 140 | +The client always sends a POST request to the server. The | |
| 141 | +general format of the POST request is as follows: | |
| 142 | 142 | |
| 143 | 143 | <blockquote><pre> |
| 144 | 144 | POST /fossil/xfer HTTP/1.0 |
| 145 | 145 | Host: fossil-scm.hwaci.com:80 |
| 146 | 146 | Content-Type: application/x-fossil |
| @@ -147,19 +147,19 @@ | ||
| 147 | 147 | Content-Length: 4216 |
| 148 | 148 | |
| 149 | 149 | <i>content...</i> |
| 150 | 150 | </pre></blockquote> |
| 151 | 151 | |
| 152 | -<p>In the example above, the pathname given after the POST keyword | |
| 152 | +In the example above, the pathname given after the POST keyword | |
| 153 | 153 | on the first line is a copy of the URL pathname. The Host: parameter |
| 154 | 154 | is also taken from the URL. The content type is always either |
| 155 | 155 | "application/x-fossil" or "application/x-fossil-debug". The "x-fossil" |
| 156 | 156 | content type is the default. The only difference is that "x-fossil" |
| 157 | 157 | content is compressed using zlib whereas "x-fossil-debug" is sent |
| 158 | -uncompressed.</p> | |
| 158 | +uncompressed. | |
| 159 | 159 | |
| 160 | -<p>A typical reply from the server might look something like this:</p> | |
| 160 | +A typical reply from the server might look something like this: | |
| 161 | 161 | |
| 162 | 162 | <blockquote><pre> |
| 163 | 163 | HTTP/1.0 200 OK |
| 164 | 164 | Date: Mon, 10 Sep 2007 12:21:01 GMT |
| 165 | 165 | Connection: close |
| @@ -168,160 +168,160 @@ | ||
| 168 | 168 | Content-Length: 265 |
| 169 | 169 | |
| 170 | 170 | <i>content...</i> |
| 171 | 171 | </pre></blockquote> |
| 172 | 172 | |
| 173 | -<p>The content type of the reply is always the same as the content type | |
| 174 | -of the request.</p> | |
| 173 | +The content type of the reply is always the same as the content type | |
| 174 | +of the request. | |
| 175 | 175 | |
| 176 | 176 | <h2>3.0 Fossil Synchronization Content</h2> |
| 177 | 177 | |
| 178 | -<p>A synchronization request between a client and server consists of | |
| 178 | +A synchronization request between a client and server consists of | |
| 179 | 179 | one or more HTTP requests as described in the previous section. This |
| 180 | -section details the "x-fossil" content type.</p> | |
| 180 | +section details the "x-fossil" content type. | |
| 181 | 181 | |
| 182 | 182 | <h3>3.1 Line-oriented Format</h3> |
| 183 | 183 | |
| 184 | -<p>The x-fossil content type consists of zero or more "cards". Cards | |
| 184 | +The x-fossil content type consists of zero or more "cards". Cards | |
| 185 | 185 | are separated by the newline character ("\n"). Leading and trailing |
| 186 | -whitespace on a card is ignored. Blank cards are ignored.</p> | |
| 186 | +whitespace on a card is ignored. Blank cards are ignored. | |
| 187 | 187 | |
| 188 | -<p>Each card is divided into zero or more space separated tokens. | |
| 188 | +Each card is divided into zero or more space separated tokens. | |
| 189 | 189 | The first token on each card is the operator. Subsequent tokens |
| 190 | 190 | are arguments. The set of operators understood by servers is slightly |
| 191 | 191 | different from the operators understood by clients, though the two |
| 192 | -are very similar.</p> | |
| 192 | +are very similar. | |
| 193 | 193 | |
| 194 | 194 | <h3>3.2 Login Cards</h3> |
| 195 | 195 | |
| 196 | -<p>Every message from client to server begins with one or more login | |
| 197 | -cards. Each login card has the following format:</p> | |
| 196 | +Every message from client to server begins with one or more login | |
| 197 | +cards. Each login card has the following format: | |
| 198 | 198 | |
| 199 | 199 | <blockquote> |
| 200 | 200 | <b>login</b> <i>userid nonce signature</i> |
| 201 | 201 | </blockquote> |
| 202 | 202 | |
| 203 | -<p>The userid is the name of the user that is requesting service | |
| 203 | +The userid is the name of the user that is requesting service | |
| 204 | 204 | from the server. The nonce is the SHA1 hash of the remainder of |
| 205 | 205 | the message - all text that follows the newline character that |
| 206 | 206 | terminates the login card. The signature is the SHA1 hash of |
| 207 | -the concatenation of the nonce and the users password.</p> | |
| 207 | +the concatenation of the nonce and the users password. | |
| 208 | 208 | |
| 209 | -<p>For each login card, the server looks up the user and verifies | |
| 209 | +For each login card, the server looks up the user and verifies | |
| 210 | 210 | that the nonce matches the SHA1 hash of the remainder of the |
| 211 | 211 | message. It then checks the signature hash to make sure the |
| 212 | 212 | signature matches. If everything |
| 213 | 213 | checks out, then the client is granted all privileges of the |
| 214 | -specified user.</p> | |
| 214 | +specified user. | |
| 215 | 215 | |
| 216 | -<p>Privileges are cumulative. There can be multiple successful | |
| 216 | +Privileges are cumulative. There can be multiple successful | |
| 217 | 217 | login cards. The session privileges are the bit-wise OR of the |
| 218 | -privileges of each individual login.</p> | |
| 218 | +privileges of each individual login. | |
| 219 | 219 | |
| 220 | 220 | <h3>3.3 File Cards</h3> |
| 221 | 221 | |
| 222 | -<p>Artifacts are transferred using either "file" cards, or "cfile" | |
| 222 | +Artifacts are transferred using either "file" cards, or "cfile" | |
| 223 | 223 | or "uvfile" cards. |
| 224 | 224 | The name "file" card comes from the fact that most artifacts correspond to |
| 225 | 225 | files that are under version control. |
| 226 | 226 | The "cfile" name is an abbreviation for "compressed file". |
| 227 | 227 | The "uvfile" name is an abbreviation for "unversioned file". |
| 228 | -</p> | |
| 228 | + | |
| 229 | 229 | |
| 230 | 230 | <h4>3.3.1 Ordinary File Cards</h4> |
| 231 | 231 | |
| 232 | -<p>For sync protocols, artifacts are transferred using "file" | |
| 232 | +For sync protocols, artifacts are transferred using "file" | |
| 233 | 233 | cards. File cards come in two different formats depending |
| 234 | 234 | on whether the artifact is sent directly or as a delta from some |
| 235 | -other artifact.</p> | |
| 235 | +other artifact. | |
| 236 | 236 | |
| 237 | 237 | <blockquote> |
| 238 | 238 | <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br> |
| 239 | 239 | <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> |
| 240 | 240 | </blockquote> |
| 241 | 241 | |
| 242 | -<p>File cards are followed by in-line "payload" data. | |
| 242 | +File cards are followed by in-line "payload" data. | |
| 243 | 243 | The content of the artifact |
| 244 | 244 | or the artifact delta is the first <i>size</i> bytes of the |
| 245 | 245 | x-fossil content that immediately follow the newline that |
| 246 | 246 | terminates the file card. |
| 247 | -</p> | |
| 248 | 247 | |
| 249 | -<p>The first argument of a file card is the ID of the artifact that | |
| 248 | + | |
| 249 | +The first argument of a file card is the ID of the artifact that | |
| 250 | 250 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 251 | 251 | representation of the name hash for the artifact. |
| 252 | 252 | The last argument of the file card is the number of bytes of |
| 253 | 253 | payload that immediately follow the file card. If the file |
| 254 | 254 | card has only two arguments, that means the payload is the |
| 255 | 255 | complete content of the artifact. If the file card has three |
| 256 | 256 | arguments, then the payload is a delta and second argument is |
| 257 | -the ID of another artifact that is the source of the delta.</p> | |
| 257 | +the ID of another artifact that is the source of the delta. | |
| 258 | 258 | |
| 259 | -<p>File cards are sent in both directions: client to server and | |
| 259 | +File cards are sent in both directions: client to server and | |
| 260 | 260 | server to client. A delta might be sent before the source of |
| 261 | 261 | the delta, so both client and server should remember deltas |
| 262 | -and be able to apply them when their source arrives.</p> | |
| 262 | +and be able to apply them when their source arrives. | |
| 263 | 263 | |
| 264 | 264 | <h4>3.3.2 Compressed File Cards</h4> |
| 265 | 265 | |
| 266 | -<p>A client that sends a clone protocol version "3" or greater will | |
| 266 | +A client that sends a clone protocol version "3" or greater will | |
| 267 | 267 | receive artifacts as "cfile" cards while cloning. This card was |
| 268 | 268 | introduced to improve the speed of the transfer of content by sending the |
| 269 | -compressed artifact directly from the server database to the client.</p> | |
| 269 | +compressed artifact directly from the server database to the client. | |
| 270 | 270 | |
| 271 | -<p>Compressed File cards are similar to File cards, sharing the same | |
| 271 | +Compressed File cards are similar to File cards, sharing the same | |
| 272 | 272 | in-line "payload" data characteristics and also the same treatment of |
| 273 | 273 | direct content or delta content. Cfile cards come in two different formats |
| 274 | 274 | depending on whether the artifact is sent directly or as a delta from |
| 275 | -some other artifact.</p> | |
| 275 | +some other artifact. | |
| 276 | 276 | |
| 277 | 277 | <blockquote> |
| 278 | 278 | <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 279 | 279 | <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 280 | 280 | </blockquote> |
| 281 | 281 | |
| 282 | -<p>The first argument of the cfile card is the ID of the artifact that | |
| 282 | +The first argument of the cfile card is the ID of the artifact that | |
| 283 | 283 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 284 | 284 | representation of the name hash for the artifact. The second argument of |
| 285 | 285 | the cfile card is the original size in bytes of the artifact. The last |
| 286 | 286 | argument of the cfile card is the number of compressed bytes of payload |
| 287 | 287 | that immediately follow the cfile card. If the cfile card has only |
| 288 | 288 | three arguments, that means the payload is the complete content of the |
| 289 | 289 | artifact. If the cfile card has four arguments, then the payload is a |
| 290 | 290 | delta and the second argument is the ID of another artifact that is the |
| 291 | 291 | source of the delta and the third argument is the original size of the |
| 292 | -delta artifact.</p> | |
| 292 | +delta artifact. | |
| 293 | 293 | |
| 294 | -<p>Unlike file cards, cfile cards are only sent in one direction during a | |
| 295 | -clone from server to client for clone protocol version "3" or greater.</p> | |
| 294 | +Unlike file cards, cfile cards are only sent in one direction during a | |
| 295 | +clone from server to client for clone protocol version "3" or greater. | |
| 296 | 296 | |
| 297 | 297 | <h4>3.3.3 Private artifacts</h4> |
| 298 | 298 | |
| 299 | -<p>"Private" content consist of artifacts that are not normally synced. | |
| 299 | +"Private" content consist of artifacts that are not normally synced. | |
| 300 | 300 | However, private content will be synced when the |
| 301 | 301 | the [/help?cmd=sync|fossil sync] command includes the "--private" option. |
| 302 | -</p> | |
| 303 | 302 | |
| 304 | -<p>Private content is marked by a "private" card: | |
| 303 | + | |
| 304 | +Private content is marked by a "private" card: | |
| 305 | 305 | |
| 306 | 306 | <blockquote> |
| 307 | 307 | <b>private</b> |
| 308 | 308 | </blockquote> |
| 309 | 309 | |
| 310 | -<p>The private card has no arguments and must directly precede a | |
| 311 | -file card that contains the private content.</p> | |
| 310 | +The private card has no arguments and must directly precede a | |
| 311 | +file card that contains the private content. | |
| 312 | 312 | |
| 313 | 313 | <h4>3.3.4 Unversioned File Cards</h4> |
| 314 | 314 | |
| 315 | -<p>Unversioned content is sent in both directions (client to server and | |
| 315 | +Unversioned content is sent in both directions (client to server and | |
| 316 | 316 | server to client) using "uvfile" cards in the following format: |
| 317 | 317 | |
| 318 | 318 | <blockquote> |
| 319 | 319 | <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i> |
| 320 | 320 | </blockquote> |
| 321 | 321 | |
| 322 | -<p>The <i>name</i> field is the name of the unversioned file. The | |
| 322 | +The <i>name</i> field is the name of the unversioned file. The | |
| 323 | 323 | <i>mtime</i> is the last modification time of the file in seconds |
| 324 | 324 | since 1970. The <i>hash</i> field is the hash of the content |
| 325 | 325 | for the unversioned file, or "<b>-</b>" for deleted content. |
| 326 | 326 | The <i>size</i> field is the (uncompressed) size of the content |
| 327 | 327 | in bytes. The <i>flags</i> field is an integer which is interpreted |
| @@ -329,234 +329,234 @@ | ||
| 329 | 329 | the <i>content</i> is to be omitted. The content might be omitted if |
| 330 | 330 | it is too large to transmit, or if the sender merely wants to update the |
| 331 | 331 | modification time of the file without changing the files content. |
| 332 | 332 | The <i>content</i> is the (uncompressed) content of the file. |
| 333 | 333 | |
| 334 | -<p>The receiver should only accept the uvfile card if the hash and | |
| 334 | +The receiver should only accept the uvfile card if the hash and | |
| 335 | 335 | size match the content and if the mtime is newer than any existing |
| 336 | 336 | instance of the same file held by the receiver. The sender will not |
| 337 | 337 | normally transmit a uvfile card unless all these constraints are true, |
| 338 | 338 | but the receiver should double-check. |
| 339 | 339 | |
| 340 | -<p>A server should only accept uvfile cards if the login user has | |
| 340 | +A server should only accept uvfile cards if the login user has | |
| 341 | 341 | the "y" write-unversioned permission. |
| 342 | 342 | |
| 343 | -<p>Servers send uvfile cards in response to uvgimme cards received from | |
| 343 | +Servers send uvfile cards in response to uvgimme cards received from | |
| 344 | 344 | the client. Clients send uvfile cards when they determine that the server |
| 345 | 345 | needs the content based on uvigot cards previously received from the server. |
| 346 | 346 | |
| 347 | 347 | <h3>3.4 Push and Pull Cards</h3> |
| 348 | 348 | |
| 349 | -<p>Among the first cards in a client-to-server message are | |
| 349 | +Among the first cards in a client-to-server message are | |
| 350 | 350 | the push and pull cards. The push card tells the server that |
| 351 | 351 | the client is pushing content. The pull card tells the server |
| 352 | 352 | that the client wants to pull content. In the event of a sync, |
| 353 | -both cards are sent. The format is as follows:</p> | |
| 353 | +both cards are sent. The format is as follows: | |
| 354 | 354 | |
| 355 | 355 | <blockquote> |
| 356 | 356 | <b>push</b> <i>servercode projectcode</i><br> |
| 357 | 357 | <b>pull</b> <i>servercode projectcode</i> |
| 358 | 358 | </blockquote> |
| 359 | 359 | |
| 360 | -<p>The <i>servercode</i> argument is the repository ID for the | |
| 360 | +The <i>servercode</i> argument is the repository ID for the | |
| 361 | 361 | client. The <i>projectcode</i> is the identifier |
| 362 | 362 | of the software project that the client repository contains. |
| 363 | 363 | The projectcode for the client and server must match in order |
| 364 | -for the transaction to proceed.</p> | |
| 364 | +for the transaction to proceed. | |
| 365 | 365 | |
| 366 | -<p>The server will also send a push card back to the client | |
| 366 | +The server will also send a push card back to the client | |
| 367 | 367 | during a clone. This is how the client determines what project |
| 368 | -code to put in the new repository it is constructing.</p> | |
| 368 | +code to put in the new repository it is constructing. | |
| 369 | 369 | |
| 370 | -<p>The <i>servercode</i> argument is currently unused. | |
| 370 | +The <i>servercode</i> argument is currently unused. | |
| 371 | 371 | |
| 372 | 372 | <h3>3.5 Clone Cards</h3> |
| 373 | 373 | |
| 374 | -<p>A clone card works like a pull card in that it is sent from | |
| 374 | +A clone card works like a pull card in that it is sent from | |
| 375 | 375 | client to server in order to tell the server that the client |
| 376 | 376 | wants to pull content. The clone card comes in two formats. Older |
| 377 | 377 | clients use the no-argument format and newer clients use the |
| 378 | -two-argument format.</p> | |
| 378 | +two-argument format. | |
| 379 | 379 | |
| 380 | 380 | <blockquote> |
| 381 | 381 | <b>clone</b><br> |
| 382 | 382 | <b>clone</b> <i>protocol-version sequence-number</i> |
| 383 | 383 | </blockquote> |
| 384 | 384 | |
| 385 | 385 | <h4>3.5.1 Protocol 3</h4> |
| 386 | 386 | |
| 387 | -<p>The latest clients send a two-argument clone message with a | |
| 387 | +The latest clients send a two-argument clone message with a | |
| 388 | 388 | protocol version of "3". (Future versions of Fossil might use larger |
| 389 | 389 | protocol version numbers.) Version "3" of the protocol enhanced version |
| 390 | 390 | "2" by introducing the "cfile" card which is intended to speed up clone |
| 391 | 391 | operations. Instead of sending "file" cards, the server will send "cfile" |
| 392 | -cards</p> | |
| 392 | +cards | |
| 393 | 393 | |
| 394 | 394 | <h4>3.5.2 Protocol 2</h4> |
| 395 | 395 | |
| 396 | -<p>The sequence-number sent is the number | |
| 396 | +The sequence-number sent is the number | |
| 397 | 397 | of artifacts received so far. For the first clone message, the |
| 398 | 398 | sequence number is 0. The server will respond by sending file |
| 399 | 399 | cards for some number of artifacts up to the maximum message size. |
| 400 | 400 | |
| 401 | -<p>The server will also send a single "clone_seqno" card to the client | |
| 401 | +The server will also send a single "clone_seqno" card to the client | |
| 402 | 402 | so that the client can know where the server left off. |
| 403 | 403 | |
| 404 | 404 | <blockquote> |
| 405 | 405 | <b>clone_seqno</b> <i>sequence-number</i> |
| 406 | 406 | </blockquote> |
| 407 | 407 | |
| 408 | -<p>The clone message in subsequent HTTP requests for the same clone | |
| 408 | +The clone message in subsequent HTTP requests for the same clone | |
| 409 | 409 | operation will use the sequence-number from the |
| 410 | -clone_seqno of the previous reply.</p> | |
| 410 | +clone_seqno of the previous reply. | |
| 411 | 411 | |
| 412 | -<p>In response to an initial clone message, the server also sends the client | |
| 412 | +In response to an initial clone message, the server also sends the client | |
| 413 | 413 | a push message so that the client can discover the projectcode for |
| 414 | -this project.</p> | |
| 414 | +this project. | |
| 415 | 415 | |
| 416 | 416 | <h4>3.5.3 Legacy Protocol</h4> |
| 417 | 417 | |
| 418 | -<p>Older clients send a clone card with no argument. The server responds | |
| 418 | +Older clients send a clone card with no argument. The server responds | |
| 419 | 419 | to a blank clone card by sending an "igot" card for every artifact in the |
| 420 | 420 | repository. The client will then issue "gimme" cards to pull down all the |
| 421 | 421 | content it needs. |
| 422 | 422 | |
| 423 | -<p>The legacy protocol works well for smaller repositories (50MB with 50,000 | |
| 423 | +The legacy protocol works well for smaller repositories (50MB with 50,000 | |
| 424 | 424 | artifacts) but is too slow and unwieldy for larger repositories. |
| 425 | 425 | The version 2 protocol is an effort to improve performance. Further |
| 426 | 426 | performance improvements with higher-numbered clone protocols are |
| 427 | 427 | possible in future versions of Fossil. |
| 428 | 428 | |
| 429 | 429 | <h3>3.6 Igot Cards</h3> |
| 430 | 430 | |
| 431 | -<p>An igot card can be sent from either client to server or from | |
| 431 | +An igot card can be sent from either client to server or from | |
| 432 | 432 | server to client in order to indicate that the sender holds a copy |
| 433 | -of a particular artifact. The format is:</p> | |
| 433 | +of a particular artifact. The format is: | |
| 434 | 434 | |
| 435 | 435 | <blockquote> |
| 436 | 436 | <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? |
| 437 | 437 | </blockquote> |
| 438 | 438 | |
| 439 | -<p>The first argument of the igot card is the ID of the artifact that | |
| 439 | +The first argument of the igot card is the ID of the artifact that | |
| 440 | 440 | the sender possesses. |
| 441 | 441 | The receiver of an igot card will typically check to see if |
| 442 | 442 | it also holds the same artifact and if not it will request the artifact |
| 443 | -using a gimme card in either the reply or in the next message.</p> | |
| 443 | +using a gimme card in either the reply or in the next message. | |
| 444 | 444 | |
| 445 | -<p>If the second argument exists and is "1", then the artifact | |
| 445 | +If the second argument exists and is "1", then the artifact | |
| 446 | 446 | identified by the first argument is private on the sender and should |
| 447 | 447 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 448 | 448 | |
| 449 | -<p>The name "igot" comes from the English slang expression "I got" meaning | |
| 449 | +The name "igot" comes from the English slang expression "I got" meaning | |
| 450 | 450 | "I have". |
| 451 | 451 | |
| 452 | 452 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 453 | 453 | |
| 454 | -<p>Zero or more "uvigot" cards are sent from server to client when | |
| 454 | +Zero or more "uvigot" cards are sent from server to client when | |
| 455 | 455 | synchronizing unversioned content. The format of a uvigot card is |
| 456 | 456 | as follows: |
| 457 | 457 | |
| 458 | 458 | <blockquote> |
| 459 | 459 | <b>uvigot</b> <i>name mtime hash size</i> |
| 460 | 460 | </blockquote> |
| 461 | 461 | |
| 462 | -<p>The <i>name</i> argument is the name of an unversioned file. | |
| 462 | +The <i>name</i> argument is the name of an unversioned file. | |
| 463 | 463 | The <i>mtime</i> is the last modification time of the unversioned file |
| 464 | 464 | in seconds since 1970. |
| 465 | 465 | The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file |
| 466 | 466 | content, or "<b>-</b>" if the file has been deleted. |
| 467 | 467 | The <i>size</i> is the uncompressed size of the file in bytes. |
| 468 | 468 | |
| 469 | -<p>When the server sees a "pragma uv-hash" card for which the hash | |
| 469 | +When the server sees a "pragma uv-hash" card for which the hash | |
| 470 | 470 | does not match, it sends uvigot cards for every unversioned file that it |
| 471 | 471 | holds. The client will use this information to figure out which |
| 472 | 472 | unversioned files need to be synchronized. |
| 473 | 473 | The server might also send a uvigot card when it receives a uvgimme card |
| 474 | 474 | but its reply message size is already oversized and hence unable to hold |
| 475 | 475 | the usual uvfile reply. |
| 476 | 476 | |
| 477 | -<p>When a client receives a "uvigot" card, it checks to see if the | |
| 477 | +When a client receives a "uvigot" card, it checks to see if the | |
| 478 | 478 | file needs to be transferred from client to server or from server to client. |
| 479 | 479 | If a client-to-server transmission is needed, the client schedules that |
| 480 | 480 | transfer to occur on a subsequent HTTP request. If a server-to-client |
| 481 | 481 | transfer is needed, then the client sends a "uvgimme" card back to the |
| 482 | 482 | server to request the file content. |
| 483 | 483 | |
| 484 | 484 | <h3>3.7 Gimme Cards</h3> |
| 485 | 485 | |
| 486 | -<p>A gimme card is sent from either client to server or from server | |
| 486 | +A gimme card is sent from either client to server or from server | |
| 487 | 487 | to client. The gimme card asks the receiver to send a particular |
| 488 | -artifact back to the sender. The format of a gimme card is this:</p> | |
| 488 | +artifact back to the sender. The format of a gimme card is this: | |
| 489 | 489 | |
| 490 | 490 | <blockquote> |
| 491 | 491 | <b>gimme</b> <i>artifact-id</i> |
| 492 | 492 | </blockquote> |
| 493 | 493 | |
| 494 | -<p>The argument to the gimme card is the ID of the artifact that | |
| 494 | +The argument to the gimme card is the ID of the artifact that | |
| 495 | 495 | the sender wants. The receiver will typically respond to a |
| 496 | 496 | gimme card by sending a file card in its reply or in the next |
| 497 | -message.</p> | |
| 497 | +message. | |
| 498 | 498 | |
| 499 | -<p>The "gimme" name means "give me". The imperative "give me" is | |
| 499 | +The "gimme" name means "give me". The imperative "give me" is | |
| 500 | 500 | pronounced as if it were a single word "gimme" in some dialects of |
| 501 | 501 | English (including the dialect spoken by the original author of Fossil). |
| 502 | 502 | |
| 503 | 503 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 504 | 504 | |
| 505 | -<p>Sync synchronizing unversioned content, the client may send "uvgimme" | |
| 505 | +Sync synchronizing unversioned content, the client may send "uvgimme" | |
| 506 | 506 | cards to the server. A uvgimme card requests that the server send |
| 507 | 507 | unversioned content to the client. The format of a uvgimme card is |
| 508 | 508 | as follows: |
| 509 | 509 | |
| 510 | 510 | <blockquote> |
| 511 | 511 | <b>uvgimme</b> <i>name</i> |
| 512 | 512 | </blockquote> |
| 513 | 513 | |
| 514 | -<p>The <i>name</i> is the name of the unversioned file found on the | |
| 514 | +The <i>name</i> is the name of the unversioned file found on the | |
| 515 | 515 | server that the client would like to have. When a server sees a |
| 516 | 516 | uvgimme card, it normally responses with a uvfile card, though it might |
| 517 | 517 | also send another uvigot card if the HTTP reply is already oversized. |
| 518 | 518 | |
| 519 | 519 | <h3>3.8 Cookie Cards</h3> |
| 520 | 520 | |
| 521 | -<p>A cookie card can be used by a server to record a small amount | |
| 521 | +A cookie card can be used by a server to record a small amount | |
| 522 | 522 | of state information on a client. The server sends a cookie to the |
| 523 | 523 | client. The client sends the same cookie back to the server on |
| 524 | 524 | its next request. The cookie card has a single argument which |
| 525 | -is its payload.</p> | |
| 525 | +is its payload. | |
| 526 | 526 | |
| 527 | 527 | <blockquote> |
| 528 | 528 | <b>cookie</b> <i>payload</i> |
| 529 | 529 | </blockquote> |
| 530 | 530 | |
| 531 | -<p>The client is not required to return the cookie to the server on | |
| 531 | +The client is not required to return the cookie to the server on | |
| 532 | 532 | its next request. Or the client might send a cookie from a different |
| 533 | 533 | server on the next request. So the server must not depend on the |
| 534 | 534 | cookie and the server must structure the cookie payload in such |
| 535 | 535 | a way that it can tell if the cookie it sees is its own cookie or |
| 536 | 536 | a cookie from another server. (Typically the server will embed |
| 537 | -its servercode as part of the cookie.)</p> | |
| 537 | +its servercode as part of the cookie.) | |
| 538 | 538 | |
| 539 | 539 | <h3>3.9 Request-Configuration Cards</h3> |
| 540 | 540 | |
| 541 | -<p>A request-configuration or "reqconfig" card is sent from client to | |
| 541 | +A request-configuration or "reqconfig" card is sent from client to | |
| 542 | 542 | server in order to request that the server send back "configuration" |
| 543 | 543 | data. "Configuration" data is information about users or website |
| 544 | 544 | appearance or other administrative details which are not part of the |
| 545 | 545 | persistent and versioned state of the project. For example, the "name" |
| 546 | 546 | of the project, the default Cascading Style Sheet (CSS) for the web-interface, |
| 547 | 547 | and the project logo displayed on the web-interface are all configuration |
| 548 | 548 | data elements. |
| 549 | 549 | |
| 550 | -<p>The reqconfig card is normally sent in response to the | |
| 550 | +The reqconfig card is normally sent in response to the | |
| 551 | 551 | "fossil configuration pull" command. The format is as follows: |
| 552 | 552 | |
| 553 | 553 | <blockquote> |
| 554 | 554 | <b>reqconfig</b> <i>configuration-name</i> |
| 555 | 555 | </blockquote> |
| 556 | 556 | |
| 557 | -<p>As of 2018-06-04, the configuration-name must be one of the | |
| 557 | +As of 2018-06-04, the configuration-name must be one of the | |
| 558 | 558 | following values: |
| 559 | 559 | |
| 560 | 560 | <table border=0 align="center"> |
| 561 | 561 | <tr><td valign="top"> |
| 562 | 562 | <ul> |
| @@ -622,84 +622,81 @@ | ||
| 622 | 622 | <li> @concealed |
| 623 | 623 | <li> @shun |
| 624 | 624 | </ul></td></tr> |
| 625 | 625 | </table> |
| 626 | 626 | |
| 627 | -<p>New configuration-names are likely to be added in future releases of | |
| 627 | +New configuration-names are likely to be added in future releases of | |
| 628 | 628 | Fossil. If the server receives a configuration-name that it does not |
| 629 | 629 | understand, the entire reqconfig card is silently ignored. The reqconfig |
| 630 | 630 | card might also be ignored if the user lacks sufficient privilege to |
| 631 | 631 | access the requested information. |
| 632 | 632 | |
| 633 | -<p>The configuration-names that begin with an alphabetic character refer | |
| 633 | +The configuration-names that begin with an alphabetic character refer | |
| 634 | 634 | to values in the "config" table of the server database. For example, |
| 635 | 635 | the "logo-image" configuration item refers to the project logo image |
| 636 | 636 | that is configured on the Admin page of the [./webui.wiki | web-interface]. |
| 637 | 637 | The value of the configuration item is returned to the client using a |
| 638 | 638 | "config" card. |
| 639 | 639 | |
| 640 | -<p>If the configuration-name begins with "@", that refers to a class of | |
| 640 | +If the configuration-name begins with "@", that refers to a class of | |
| 641 | 641 | values instead of a single value. The content of these configuration items |
| 642 | 642 | is returned in a "config" card that contains pure SQL text that is |
| 643 | 643 | intended to be evaluated by the client. |
| 644 | 644 | |
| 645 | -<p>The @user and @concealed configuration items contain sensitive information | |
| 645 | +The @user and @concealed configuration items contain sensitive information | |
| 646 | 646 | and are ignored for clients without sufficient privilege. |
| 647 | 647 | |
| 648 | 648 | <h3>3.10 Configuration Cards</h3> |
| 649 | 649 | |
| 650 | -<p>A "config" card is used to send configuration information from client | |
| 650 | +A "config" card is used to send configuration information from client | |
| 651 | 651 | to server (in response to a "fossil configuration push" command) or |
| 652 | 652 | from server to client (in response to a "fossil configuration pull" or |
| 653 | 653 | "fossil clone" command). The format is as follows: |
| 654 | 654 | |
| 655 | 655 | <blockquote> |
| 656 | 656 | <b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i> |
| 657 | 657 | </blockquote> |
| 658 | 658 | |
| 659 | -<p>The server will only accept a config card if the user has | |
| 659 | +The server will only accept a config card if the user has | |
| 660 | 660 | "Admin" privilege. A client will only accept a config card if |
| 661 | 661 | it had sent a corresponding reqconfig card in its request. |
| 662 | 662 | |
| 663 | -<p>The content of the configuration item is used to overwrite the | |
| 663 | +The content of the configuration item is used to overwrite the | |
| 664 | 664 | corresponding configuration data in the receiver. |
| 665 | 665 | |
| 666 | 666 | <h3>3.11 Pragma Cards</h3> |
| 667 | 667 | |
| 668 | -<p>The client may try to influence the behavior of the server by | |
| 668 | +The client may try to influence the behavior of the server by | |
| 669 | 669 | issuing a pragma card: |
| 670 | 670 | |
| 671 | 671 | <blockquote> |
| 672 | 672 | <b>pragma</i> <i>name value...</i> |
| 673 | 673 | </blockquote> |
| 674 | 674 | |
| 675 | -<p>The "pragma" card has at least one argument which is the pragma name. | |
| 675 | +The "pragma" card has at least one argument which is the pragma name. | |
| 676 | 676 | The pragma name defines what the pragma does. |
| 677 | 677 | A pragma might have zero or more "value" arguments |
| 678 | 678 | depending on the pragma name. |
| 679 | 679 | |
| 680 | -<p>New pragma names may be added to the protocol from time to time | |
| 680 | +New pragma names may be added to the protocol from time to time | |
| 681 | 681 | in order to enhance the capabilities of Fossil. |
| 682 | 682 | Unknown pragmas are silently ignored, for backwards compatibility. |
| 683 | 683 | |
| 684 | -<p>The following are the known pragma names as of 2019-06-30: | |
| 684 | +The following are the known pragma names as of 2019-06-30: | |
| 685 | 685 | |
| 686 | 686 | <ol> |
| 687 | -<li><p><b>send-private</b> | |
| 688 | -<p>The send-private pragma instructs the server to send all of its | |
| 687 | +<li><b>send-private</b> The send-private pragma instructs the server to send all of its | |
| 689 | 688 | private artifacts to the client. The server will only obey this |
| 690 | 689 | request if the user has the "x" or "Private" privilege. |
| 691 | 690 | |
| 692 | -<li><p><b>send-catalog</b> | |
| 693 | -<p>The send-catalog pragma instructs the server to transmit igot | |
| 691 | +<li><b>send-catalog</b> The send-catalog pragma instructs the server to transmit igot | |
| 694 | 692 | cards for every known artifact. This can help the client and server |
| 695 | 693 | to get back in synchronization after a prior protocol error. The |
| 696 | 694 | "--verily" option to the [/help?cmd=sync|fossil sync] command causes |
| 697 | -the send-catalog pragma to be transmitted.</p> | |
| 695 | +the send-catalog pragma to be transmitted. | |
| 698 | 696 | |
| 699 | -<li><p><b>uv-hash</b> <i>HASH</i> | |
| 700 | -<p>The uv-hash pragma is sent from client to server to provoke a | |
| 697 | +<li><b>uv-hash</b> <i>HASH</i> The uv-hash pragma is sent from client to server to provoke a | |
| 701 | 698 | synchronization of unversioned content. The <i>HASH</i> is a SHA1 |
| 702 | 699 | hash of the names, modification times, and individual hashes of all |
| 703 | 700 | unversioned files on the client. If the unversioned content hash |
| 704 | 701 | from the client does not match the unversioned content hash on the |
| 705 | 702 | server, then the server will reply with either a "pragma uv-push-ok" |
| @@ -707,157 +704,151 @@ | ||
| 707 | 704 | each unversioned file currently held on the server. The collection |
| 708 | 705 | of "uvigot" cards sent in response to a "uv-hash" pragma is called |
| 709 | 706 | the "unversioned catalog". The client will used the unversioned |
| 710 | 707 | catalog to figure out which files (if any) need to be synchronized |
| 711 | 708 | between client and server and send appropriate "uvfile" or "uvgimme" |
| 712 | -cards on the next HTTP request.</p> | |
| 709 | +cards on the next HTTP request. | |
| 713 | 710 | |
| 714 | -<p>If a client sends a uv-hash pragma and does not receive back | |
| 711 | +If a client sends a uv-hash pragma and does not receive back | |
| 715 | 712 | either a uv-pull-only or uv-push-ok pragma, that means that the |
| 716 | 713 | content on the server exactly matches the content on the client and |
| 717 | 714 | no further synchronization is required. |
| 718 | 715 | |
| 719 | -<li><p><b>uv-pull-only</b></i> | |
| 720 | -<p>A server sends the uv-pull-only pragma to the client in response | |
| 716 | +<li><b>uv-pull-only</b></i> A server sends the uv-pull-only pragma to the client in response | |
| 721 | 717 | to a uv-hash pragma with a mismatched content hash argument. This |
| 722 | 718 | pragma indicates that there are differences in unversioned content |
| 723 | 719 | between the client and server but that content can only be transferred |
| 724 | 720 | from server to client. The server is unwilling to accept content from |
| 725 | 721 | the client because the client login lacks the "write-unversioned" |
| 726 | -permission.</p> | |
| 722 | +permission. | |
| 727 | 723 | |
| 728 | -<li><p><b>uv-push-ok</b></i> | |
| 729 | -<p>A server sends the uv-push-ok pragma to the client in response | |
| 724 | +<li><b>uv-push-ok</b></i> A server sends the uv-push-ok pragma to the client in response | |
| 730 | 725 | to a uv-hash pragma with a mismatched content hash argument. This |
| 731 | 726 | pragma indicates that there are differences in unversioned content |
| 732 | 727 | between the client and server and that content can be transferred |
| 733 | 728 | in either direction. The server is willing to accept content from |
| 734 | 729 | the client because the client login has the "write-unversioned" |
| 735 | -permission.</p> | |
| 730 | +permission. | |
| 736 | 731 | |
| 737 | -<li><p><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i></p> | |
| 738 | -<p>A client sends the "ci-lock" pragma to the server to indicate | |
| 732 | +<li><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i> A client sends the "ci-lock" pragma to the server to indicate | |
| 739 | 733 | that it is about to add a new check-in as a child of the |
| 740 | 734 | CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH. |
| 741 | 735 | If some other client has already indicated that it was also |
| 742 | 736 | trying to commit against CHECKIN-HASH, that indicates that a |
| 743 | 737 | fork is about to occur, and the server will reply with |
| 744 | 738 | a "ci-lock-fail" pragma (see below). Check-in locks |
| 745 | 739 | automatically expire when the check-in actually occurs, or |
| 746 | 740 | after a timeout (currently one minute but subject to change). |
| 747 | 741 | |
| 748 | -<li><p><b>ci-lock-fail</b> <i>LOGIN MTIME</i></p> | |
| 749 | -<p>When a server receives two or more "ci-lock" pragma messages | |
| 742 | +<li><b>ci-lock-fail</b> <i>LOGIN MTIME</i> When a server receives two or more "ci-lock" pragma messages | |
| 750 | 743 | for the same check-in but from different clients, the second a |
| 751 | 744 | subsequent ci-lock will provoke a ci-lock-fail pragma in the |
| 752 | 745 | reply to let the client know that it if continues with the |
| 753 | 746 | check-in it will likely generate a fork. The LOGIN and MTIME |
| 754 | 747 | arguments are intended to provide information to the client to |
| 755 | 748 | help it generate a more useful error message. |
| 756 | -</p> | |
| 757 | 749 | |
| 758 | -<li><p><b>ci-unlock</b> <i>CLIENT-ID</i></p> | |
| 759 | -<p>A client sends the "ci-unlock" pragma to the server after | |
| 750 | +<li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after | |
| 760 | 751 | a successful commit. This instructs the server to release |
| 761 | 752 | any lock on any check-in previously held by that client. |
| 762 | 753 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 763 | 754 | that might arise if a check-in is aborted and then restarted |
| 764 | 755 | on a branch. |
| 765 | 756 | </ol> |
| 766 | 757 | |
| 767 | 758 | <h3>3.12 Comment Cards</h3> |
| 768 | 759 | |
| 769 | -<p>Any card that begins with "#" (ASCII 0x23) is a comment card and | |
| 770 | -is silently ignored.</p> | |
| 760 | +Any card that begins with "#" (ASCII 0x23) is a comment card and | |
| 761 | +is silently ignored. | |
| 771 | 762 | |
| 772 | 763 | <h3>3.13 Message and Error Cards</h3> |
| 773 | 764 | |
| 774 | -<p>If the server discovers anything wrong with a request, it generates | |
| 765 | +If the server discovers anything wrong with a request, it generates | |
| 775 | 766 | an error card in its reply. When the client sees the error card, |
| 776 | 767 | it displays an error message to the user and aborts the sync |
| 777 | -operation. An error card looks like this:</p> | |
| 768 | +operation. An error card looks like this: | |
| 778 | 769 | |
| 779 | 770 | <blockquote> |
| 780 | 771 | <b>error</b> <i>error-message</i> |
| 781 | 772 | </blockquote> |
| 782 | 773 | |
| 783 | -<p>The error message is English text that is encoded in order to | |
| 774 | +The error message is English text that is encoded in order to | |
| 784 | 775 | be a single token. |
| 785 | 776 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 786 | 777 | newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash |
| 787 | 778 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 788 | 779 | space and newline, no other whitespace characters nor any |
| 789 | 780 | unprintable characters are allowed in |
| 790 | -the error message.</p> | |
| 781 | +the error message. | |
| 791 | 782 | |
| 792 | -<p>The server can also send a message card that also prints a | |
| 783 | +The server can also send a message card that also prints a | |
| 793 | 784 | message on the client console, but which is not an error: |
| 794 | 785 | |
| 795 | 786 | <blockquote> |
| 796 | 787 | <b>message</b> <i>message-text</i> |
| 797 | 788 | </blockquote> |
| 798 | 789 | |
| 799 | -<p>The message-text uses the same format as an error message. | |
| 790 | +The message-text uses the same format as an error message. | |
| 800 | 791 | |
| 801 | 792 | <h3>3.14 Unknown Cards</h3> |
| 802 | 793 | |
| 803 | -<p>If either the client or the server sees a card that is not | |
| 804 | -described above, then it generates an error and aborts.</p> | |
| 794 | +If either the client or the server sees a card that is not | |
| 795 | +described above, then it generates an error and aborts. | |
| 805 | 796 | |
| 806 | 797 | <h2>4.0 Phantoms And Clusters</h2> |
| 807 | 798 | |
| 808 | -<p>When a repository knows that an artifact exists and knows the ID of | |
| 799 | +When a repository knows that an artifact exists and knows the ID of | |
| 809 | 800 | that artifact, but it does not know the artifact content, then it stores that |
| 810 | 801 | artifact as a "phantom". A repository will typically create a phantom when |
| 811 | 802 | it receives an igot card for an artifact that it does not hold or when it |
| 812 | 803 | receives a file card that references a delta source that it does not |
| 813 | 804 | hold. When a server is generating its reply or when a client is |
| 814 | 805 | generating a new request, it will usually send gimme cards for every |
| 815 | -phantom that it holds.</p> | |
| 806 | +phantom that it holds. | |
| 816 | 807 | |
| 817 | -<p>A cluster is a special artifact that tells of the existence of other | |
| 808 | +A cluster is a special artifact that tells of the existence of other | |
| 818 | 809 | artifacts. Any artifact in the repository that follows the syntactic rules |
| 819 | -of a cluster is considered a cluster.</p> | |
| 810 | +of a cluster is considered a cluster. | |
| 820 | 811 | |
| 821 | -<p>A cluster is line oriented. Each line of a cluster | |
| 812 | +A cluster is line oriented. Each line of a cluster | |
| 822 | 813 | is a card. The cards are separated by the newline ("\n") character. |
| 823 | 814 | Each card consists of a single character card type, a space, and a |
| 824 | 815 | single argument. No extra whitespace and no trailing or leading |
| 825 | 816 | whitespace is allowed. All cards in the cluster must occur in |
| 826 | -strict lexicographical order.</p> | |
| 817 | +strict lexicographical order. | |
| 827 | 818 | |
| 828 | -<p>A cluster consists of one or more "M" cards followed by a single | |
| 819 | +A cluster consists of one or more "M" cards followed by a single | |
| 829 | 820 | "Z" card. Each M card holds an argument which is an artifact ID for an |
| 830 | 821 | artifact in the repository. The Z card has a single argument which is the |
| 831 | 822 | lower-case hexadecimal representation of the MD5 checksum of all |
| 832 | 823 | preceding M cards up to and included the newline character that |
| 833 | -occurred just before the Z that starts the Z card.</p> | |
| 824 | +occurred just before the Z that starts the Z card. | |
| 834 | 825 | |
| 835 | -<p>Any artifact that does not match the specifications of a cluster | |
| 826 | +Any artifact that does not match the specifications of a cluster | |
| 836 | 827 | exactly is not a cluster. There must be no extra whitespace in |
| 837 | 828 | the artifact. There must be one or more M cards. There must be a |
| 838 | 829 | single Z card with a correct MD5 checksum. And all cards must |
| 839 | -be in strict lexicographical order.</p> | |
| 830 | +be in strict lexicographical order. | |
| 840 | 831 | |
| 841 | 832 | <h3>4.1 The Unclustered Table</h3> |
| 842 | 833 | |
| 843 | -<p>Every repository maintains a table named "<b>unclustered</b>" | |
| 834 | +Every repository maintains a table named "<b>unclustered</b>" | |
| 844 | 835 | which records the identity of every artifact and phantom it holds that is not |
| 845 | 836 | mentioned in a cluster. The entries in the unclustered table can |
| 846 | 837 | be thought of as leaves on a tree of artifacts. Some of the unclustered |
| 847 | 838 | artifacts will be other clusters. Those clusters may contain other clusters, |
| 848 | 839 | which might contain still more clusters, and so forth. Beginning |
| 849 | 840 | with the artifacts in the unclustered table, one can follow the chain |
| 850 | -of clusters to find every artifact in the repository.</p> | |
| 841 | +of clusters to find every artifact in the repository. | |
| 851 | 842 | |
| 852 | 843 | <h2>5.0 Synchronization Strategies</h2> |
| 853 | 844 | |
| 854 | 845 | <h3>5.1 Pull</h3> |
| 855 | 846 | |
| 856 | -<p>A typical pull operation proceeds as shown below. Details | |
| 847 | +A typical pull operation proceeds as shown below. Details | |
| 857 | 848 | of the actual implementation may very slightly but the gist of |
| 858 | -a pull is captured in the following steps:</p> | |
| 849 | +a pull is captured in the following steps: | |
| 859 | 850 | |
| 860 | 851 | <ol> |
| 861 | 852 | <li>The client sends login and pull cards. |
| 862 | 853 | <li>The client sends a cookie card if it has previously received a cookie. |
| 863 | 854 | <li>The client sends gimme cards for every phantom that it holds. |
| @@ -877,38 +868,38 @@ | ||
| 877 | 868 | that mentions an artifact that the client does not possess. |
| 878 | 869 | <li>The client creates a phantom for the delta source of file cards when |
| 879 | 870 | the delta source is an artifact that the client does not possess. |
| 880 | 871 | </ol> |
| 881 | 872 | |
| 882 | -<p>These ten steps represent a single HTTP round-trip request. | |
| 873 | +These ten steps represent a single HTTP round-trip request. | |
| 883 | 874 | The first three steps are the processing that occurs on the client |
| 884 | 875 | to generate the request. The middle four steps are processing |
| 885 | 876 | that occurs on the server to interpret the request and generate a |
| 886 | 877 | reply. And the last three steps are the processing that the |
| 887 | -client does to interpret the reply.</p> | |
| 878 | +client does to interpret the reply. | |
| 888 | 879 | |
| 889 | -<p>During a pull, the client will keep sending HTTP requests | |
| 890 | -until it holds all artifacts that exist on the server.</p> | |
| 880 | +During a pull, the client will keep sending HTTP requests | |
| 881 | +until it holds all artifacts that exist on the server. | |
| 891 | 882 | |
| 892 | -<p>Note that the server tries | |
| 883 | +Note that the server tries | |
| 893 | 884 | to limit the size of its reply message to something reasonable |
| 894 | 885 | (usually about 1MB) so that it might stop sending file cards as |
| 895 | -described in step (6) if the reply becomes too large.</p> | |
| 886 | +described in step (6) if the reply becomes too large. | |
| 896 | 887 | |
| 897 | -<p>Step (5) is the only way in which new clusters can be created. | |
| 888 | +Step (5) is the only way in which new clusters can be created. | |
| 898 | 889 | By only creating clusters on the server, we hope to minimize the |
| 899 | 890 | amount of overlap between clusters in the common configuration where |
| 900 | 891 | there is a single server and many clients. The same synchronization |
| 901 | 892 | protocol will continue to work even if there are multiple servers |
| 902 | 893 | or if servers and clients sometimes change roles. The only negative |
| 903 | 894 | effects of these unusual arrangements is that more than the minimum |
| 904 | -number of clusters might be generated.</p> | |
| 895 | +number of clusters might be generated. | |
| 905 | 896 | |
| 906 | 897 | <h3>5.2 Push</h3> |
| 907 | 898 | |
| 908 | -<p>A typical push operation proceeds roughly as shown below. As | |
| 909 | -with a pull, the actual implementation may vary slightly.</p> | |
| 899 | +A typical push operation proceeds roughly as shown below. As | |
| 900 | +with a pull, the actual implementation may vary slightly. | |
| 910 | 901 | |
| 911 | 902 | <ol> |
| 912 | 903 | <li>The client sends login and push cards. |
| 913 | 904 | <li>The client sends file cards for any artifacts that it holds that have |
| 914 | 905 | never before been pushed - artifacts that come from local check-ins. |
| @@ -929,40 +920,40 @@ | ||
| 929 | 920 | <hr> |
| 930 | 921 | <li>The client remembers the gimme cards from the server so that it |
| 931 | 922 | can generate file cards in reply on the next cycle. |
| 932 | 923 | </ol> |
| 933 | 924 | |
| 934 | -<p>As with a pull, the steps of a push operation repeat until the | |
| 925 | +As with a pull, the steps of a push operation repeat until the | |
| 935 | 926 | server knows all artifacts that exist on the client. Also, as with |
| 936 | 927 | pull, the client attempts to keep the size of the request from |
| 937 | 928 | growing too large by suppressing file cards once the |
| 938 | -size of the request reaches 1MB.</p> | |
| 929 | +size of the request reaches 1MB. | |
| 939 | 930 | |
| 940 | 931 | <h3 id="sync">5.3 Sync</h3> |
| 941 | 932 | |
| 942 | -<p>A sync is just a pull and a push that happen at the same time. | |
| 933 | +A sync is just a pull and a push that happen at the same time. | |
| 943 | 934 | The first three steps of a pull are combined with the first five steps |
| 944 | 935 | of a push. Steps (4) through (7) of a pull are combined with steps |
| 945 | 936 | (5) through (8) of a push. And steps (8) through (10) of a pull |
| 946 | -are combined with step (9) of a push.</p> | |
| 937 | +are combined with step (9) of a push. | |
| 947 | 938 | |
| 948 | 939 | <h3>5.4 Unversioned File Sync</h3> |
| 949 | 940 | |
| 950 | -<p>"Unversioned files" are files held in the repository | |
| 941 | +"Unversioned files" are files held in the repository | |
| 951 | 942 | where only the most recent version of the file is kept rather than |
| 952 | 943 | the entire change history. Unversioned files are intended to be |
| 953 | 944 | used to store ephemeral content, such as compiled binaries of the |
| 954 | 945 | most recent release. |
| 955 | 946 | |
| 956 | -<p>Unversioned files are identified by name and timestamp (mtime). | |
| 947 | +Unversioned files are identified by name and timestamp (mtime). | |
| 957 | 948 | Only the most recent version of each file (the version with |
| 958 | 949 | the largest mtime value) is retained. |
| 959 | 950 | |
| 960 | -<p>Unversioned files are synchronized using the | |
| 951 | +Unversioned files are synchronized using the | |
| 961 | 952 | [/help?cmd=unversioned|fossil unversioned sync] command. |
| 962 | 953 | |
| 963 | -<p>A schematic of an unversioned file synchronization is as follows: | |
| 954 | +A schematic of an unversioned file synchronization is as follows: | |
| 964 | 955 | |
| 965 | 956 | <ol> |
| 966 | 957 | <li>The client sends a "pragma uv-hash" card to the server. The argument |
| 967 | 958 | to the uv-hash pragma is a hash of all filesnames, mtimes, and |
| 968 | 959 | content hashes for the unversioned files held by the client. |
| @@ -981,17 +972,17 @@ | ||
| 981 | 972 | <hr> |
| 982 | 973 | <li>The server updates its unversioned file store with received "uvfile" |
| 983 | 974 | cards and answers "uvgimme" cards with "uvfile" cards in its reply. |
| 984 | 975 | </ol> |
| 985 | 976 | |
| 986 | -<p>The last two steps might be repeated multiple | |
| 977 | +The last two steps might be repeated multiple | |
| 987 | 978 | times if there is more unversioned content to be transferred than will |
| 988 | 979 | fit comfortably in a single HTTP request. |
| 989 | 980 | |
| 990 | 981 | <h2>6.0 Summary</h2> |
| 991 | 982 | |
| 992 | -<p>Here are the key points of the synchronization protocol:</p> | |
| 983 | +Here are the key points of the synchronization protocol: | |
| 993 | 984 | |
| 994 | 985 | <ol> |
| 995 | 986 | <li>The client sends one or more PUSH HTTP requests to the server. |
| 996 | 987 | The request and reply content type is "application/x-fossil". |
| 997 | 988 | <li>HTTP request content is compressed using zlib. |
| @@ -1031,11 +1022,11 @@ | ||
| 1031 | 1022 | gimme messages for those artifacts. |
| 1032 | 1023 | </ol> |
| 1033 | 1024 | |
| 1034 | 1025 | <h2>7.0 Troubleshooting And Debugging Hints</h2> |
| 1035 | 1026 | |
| 1036 | -<p> | |
| 1027 | + | |
| 1037 | 1028 | If you run the [/help?cmd=sync|fossil sync] command |
| 1038 | 1029 | (or [/help?cmd=pull|pull] or [/help?cmd=push|push] or |
| 1039 | 1030 | [/help?cmd=clone|clone]) with the --httptrace option, Fossil |
| 1040 | 1031 | will keep a copy of each HTTP request and reply in files |
| 1041 | 1032 | named: |
| @@ -1042,17 +1033,17 @@ | ||
| 1042 | 1033 | <ul> |
| 1043 | 1034 | <li> <tt>http-request-</tt><i>N</i><tt>.txt</tt> |
| 1044 | 1035 | <li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt> |
| 1045 | 1036 | </ul> |
| 1046 | 1037 | |
| 1047 | -<p>In the above, <i>N</i> is an integer that increments with each | |
| 1038 | +In the above, <i>N</i> is an integer that increments with each | |
| 1048 | 1039 | round-trip. If you are having trouble on the server side, |
| 1049 | 1040 | you can run the "[/help?cmd=test-http|fossil test-http]" command in a |
| 1050 | 1041 | debugger using one the "http-request-N.txt" files as input and |
| 1051 | 1042 | single step through the processing performed by the server. |
| 1052 | 1043 | |
| 1053 | -<p>The "--transport-command CMD" option on [/help?cmd=sync|fossil sync] | |
| 1044 | +The "--transport-command CMD" option on [/help?cmd=sync|fossil sync] | |
| 1054 | 1045 | (and similar) causes the external program "CMD" to be used to move |
| 1055 | 1046 | the sync message to the server and retrieve the sync reply. The |
| 1056 | 1047 | CMD is given three arguments: |
| 1057 | 1048 | <ol> |
| 1058 | 1049 | <li> The URL of the server |
| @@ -1060,9 +1051,9 @@ | ||
| 1060 | 1051 | protocol text, with the HTTP headers |
| 1061 | 1052 | <li> The name of a temporary file into which the CMD should write the |
| 1062 | 1053 | reply sync protocol text, again without any HTTP headers |
| 1063 | 1054 | </ol> |
| 1064 | 1055 | |
| 1065 | -<p>In a complex debugging situation, you can run the command | |
| 1056 | +In a complex debugging situation, you can run the command | |
| 1066 | 1057 | "fossil sync --transport-command ./debugging_script" where |
| 1067 | 1058 | "debugging_script" is some script of your own that invokes |
| 1068 | 1059 | the anomolous behavior your are trying to debug. |
| 1069 | 1060 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -1,13 +1,13 @@ | |
| 1 | <title>The Fossil Sync Protocol</title> |
| 2 | |
| 3 | <p>This document describes the wire protocol used to synchronize |
| 4 | content between two Fossil repositories.</p> |
| 5 | |
| 6 | <h2>1.0 Overview</h2> |
| 7 | |
| 8 | <p>The global state of a fossil repository consists of an unordered |
| 9 | collection of artifacts. Each artifact is identified by a cryptographic |
| 10 | hash of its content, expressed as a lower-case hexadecimal string. |
| 11 | Synchronization is the process of sharing artifacts between |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| @@ -17,13 +17,13 @@ | |
| 17 | of hashes for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred.</p> |
| 23 | |
| 24 | <p>Each repository also has local state. The local state determines |
| 25 | the web-page formatting preferences, authorized users, ticket formats, |
| 26 | and similar information that varies from one repository to another. |
| 27 | The local state is not usually transferred during a sync. Except, |
| 28 | some local state is transferred during a [/help?cmd=clone|clone] |
| 29 | in order to initialize the local state of the new repository. Also, |
| @@ -32,67 +32,67 @@ | |
| 32 | [/help?cmd=configuration|config pull] |
| 33 | commands. |
| 34 | |
| 35 | <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> |
| 36 | |
| 37 | <p>The "bag of artifacts" data model used by Fossil is apparently an |
| 38 | implementation of a particular |
| 39 | [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free |
| 40 | Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The |
| 41 | academic literature on CRDTs only began to appear in about 2011, and |
| 42 | Fossil predates that research by at least 4 years. But it is nice to |
| 43 | know that theorists have now proven that the underlying data model of |
| 44 | Fossil can provide strongly-consistent replicas using only |
| 45 | peer-to-peer communication and without any kind of central |
| 46 | authority.</p> |
| 47 | |
| 48 | <p>If you are already familiar with CRDTs and were wondering if Fossil |
| 49 | used them, the answer is "yes". We just don't call them by that name.</p> |
| 50 | |
| 51 | |
| 52 | <h2>2.0 Transport</h2> |
| 53 | |
| 54 | <p>All communication between client and server is via HTTP requests. |
| 55 | The server is listening for incoming HTTP requests. The client |
| 56 | issues one or more HTTP requests and receives replies for each |
| 57 | request.</p> |
| 58 | |
| 59 | <p>The server might be running as an independent server |
| 60 | using the <b>server</b> command, or it might be launched from |
| 61 | inetd or xinetd using the <b>http</b> command. Or the server might |
| 62 | be launched from CGI. |
| 63 | (See "[./server/|How To Configure A Fossil Server]" for details.) |
| 64 | The specifics of how the server listens |
| 65 | for incoming HTTP requests is immaterial to this protocol. |
| 66 | The important point is that the server is listening for requests and |
| 67 | the client is the issuer of the requests.</p> |
| 68 | |
| 69 | <p>A single push, pull, or sync might involve multiple HTTP requests. |
| 70 | The client maintains state between all requests. But on the server |
| 71 | side, each request is independent. The server does not preserve |
| 72 | any information about the client from one request to the next.</p> |
| 73 | |
| 74 | <p>Note: Throughout this article, we use the terms "server" and "client" |
| 75 | to represent the listener and initiator of the interaction, respectively. |
| 76 | Nothing in this protocol requires that the server actually be a back-room |
| 77 | processor housed in a datacenter, nor does the client need to be a desktop |
| 78 | or handheld device. For the purposes of this article "client" simply means |
| 79 | the repository that initiates the conversation and "server" is the repository |
| 80 | that responds. Nothing more.</p> |
| 81 | |
| 82 | <h4>2.0.1 HTTPS Transport</h4> |
| 83 | |
| 84 | <p>In the current implementation of Fossil, the server only |
| 85 | understands HTTP requests. The client can send either |
| 86 | clear-text HTTP requests or encrypted HTTPS requests. But when |
| 87 | HTTPS requests are sent, they first must be decrypted by a web server |
| 88 | or proxy before being passed to the Fossil server. This limitation |
| 89 | may be relaxed in a future release.</p> |
| 90 | |
| 91 | <h4>2.0.2 SSH Transport</h4> |
| 92 | |
| 93 | <p>When doing a sync using an "ssh:..." URL, the same HTTP transport protocol |
| 94 | is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh] |
| 95 | to start an instance of the [/help?cmd=test-http|fossil test-http] command |
| 96 | running on the remote machine. It then sends HTTP requests and gets back HTTP |
| 97 | replies over the SSH connection, rather than sending and receiving over an |
| 98 | internet socket. To see the specific "ssh" command that the Fossil client |
| @@ -99,11 +99,11 @@ | |
| 99 | runs in order to set up a connection, add either of the the "--httptrace" or |
| 100 | "--sshtrace" options to the "fossil sync" command line. |
| 101 | |
| 102 | <h4>2.0.3 FILE Transport</h4> |
| 103 | |
| 104 | <p>When doing a sync using a "file:..." URL, the same HTTP protocol is |
| 105 | still used. But instead of sending each HTTP request over a socket or |
| 106 | via SSH, the HTTP request is written into a temporary file. The client |
| 107 | then invokes the [/help?cmd=http|fossil http] command in a subprocess |
| 108 | to process the request and and generate a reply. The client then reads |
| 109 | the HTTP reply out of a temporary file on disk, and deletes the two |
| @@ -111,36 +111,36 @@ | |
| 111 | in order to implement the "file:" transport, add the "--httptrace" |
| 112 | option to the "fossil sync" command. |
| 113 | |
| 114 | <h3>2.1 Server Identification</h3> |
| 115 | |
| 116 | <p>The server is identified by a URL argument that accompanies the |
| 117 | push, pull, or sync command on the client. (As a convenience to |
| 118 | users, the URL can be omitted on the client command and the same URL |
| 119 | from the most recent push, pull, or sync will be reused. This saves |
| 120 | typing in the common case where the client does multiple syncs to |
| 121 | the same server.)</p> |
| 122 | |
| 123 | <p>The client modifies the URL by appending the method name "<b>/xfer</b>" |
| 124 | to the end. For example, if the URL specified on the client command |
| 125 | line is</p> |
| 126 | |
| 127 | <blockquote> |
| 128 | https://fossil-scm.org/fossil |
| 129 | </blockquote> |
| 130 | |
| 131 | <p>Then the URL that is really used to do the synchronization will |
| 132 | be:</p> |
| 133 | |
| 134 | <blockquote> |
| 135 | https://fossil-scm.org/fossil/xfer |
| 136 | </blockquote> |
| 137 | |
| 138 | <h3>2.2 HTTP Request Format</h3> |
| 139 | |
| 140 | <p>The client always sends a POST request to the server. The |
| 141 | general format of the POST request is as follows:</p> |
| 142 | |
| 143 | <blockquote><pre> |
| 144 | POST /fossil/xfer HTTP/1.0 |
| 145 | Host: fossil-scm.hwaci.com:80 |
| 146 | Content-Type: application/x-fossil |
| @@ -147,19 +147,19 @@ | |
| 147 | Content-Length: 4216 |
| 148 | |
| 149 | <i>content...</i> |
| 150 | </pre></blockquote> |
| 151 | |
| 152 | <p>In the example above, the pathname given after the POST keyword |
| 153 | on the first line is a copy of the URL pathname. The Host: parameter |
| 154 | is also taken from the URL. The content type is always either |
| 155 | "application/x-fossil" or "application/x-fossil-debug". The "x-fossil" |
| 156 | content type is the default. The only difference is that "x-fossil" |
| 157 | content is compressed using zlib whereas "x-fossil-debug" is sent |
| 158 | uncompressed.</p> |
| 159 | |
| 160 | <p>A typical reply from the server might look something like this:</p> |
| 161 | |
| 162 | <blockquote><pre> |
| 163 | HTTP/1.0 200 OK |
| 164 | Date: Mon, 10 Sep 2007 12:21:01 GMT |
| 165 | Connection: close |
| @@ -168,160 +168,160 @@ | |
| 168 | Content-Length: 265 |
| 169 | |
| 170 | <i>content...</i> |
| 171 | </pre></blockquote> |
| 172 | |
| 173 | <p>The content type of the reply is always the same as the content type |
| 174 | of the request.</p> |
| 175 | |
| 176 | <h2>3.0 Fossil Synchronization Content</h2> |
| 177 | |
| 178 | <p>A synchronization request between a client and server consists of |
| 179 | one or more HTTP requests as described in the previous section. This |
| 180 | section details the "x-fossil" content type.</p> |
| 181 | |
| 182 | <h3>3.1 Line-oriented Format</h3> |
| 183 | |
| 184 | <p>The x-fossil content type consists of zero or more "cards". Cards |
| 185 | are separated by the newline character ("\n"). Leading and trailing |
| 186 | whitespace on a card is ignored. Blank cards are ignored.</p> |
| 187 | |
| 188 | <p>Each card is divided into zero or more space separated tokens. |
| 189 | The first token on each card is the operator. Subsequent tokens |
| 190 | are arguments. The set of operators understood by servers is slightly |
| 191 | different from the operators understood by clients, though the two |
| 192 | are very similar.</p> |
| 193 | |
| 194 | <h3>3.2 Login Cards</h3> |
| 195 | |
| 196 | <p>Every message from client to server begins with one or more login |
| 197 | cards. Each login card has the following format:</p> |
| 198 | |
| 199 | <blockquote> |
| 200 | <b>login</b> <i>userid nonce signature</i> |
| 201 | </blockquote> |
| 202 | |
| 203 | <p>The userid is the name of the user that is requesting service |
| 204 | from the server. The nonce is the SHA1 hash of the remainder of |
| 205 | the message - all text that follows the newline character that |
| 206 | terminates the login card. The signature is the SHA1 hash of |
| 207 | the concatenation of the nonce and the users password.</p> |
| 208 | |
| 209 | <p>For each login card, the server looks up the user and verifies |
| 210 | that the nonce matches the SHA1 hash of the remainder of the |
| 211 | message. It then checks the signature hash to make sure the |
| 212 | signature matches. If everything |
| 213 | checks out, then the client is granted all privileges of the |
| 214 | specified user.</p> |
| 215 | |
| 216 | <p>Privileges are cumulative. There can be multiple successful |
| 217 | login cards. The session privileges are the bit-wise OR of the |
| 218 | privileges of each individual login.</p> |
| 219 | |
| 220 | <h3>3.3 File Cards</h3> |
| 221 | |
| 222 | <p>Artifacts are transferred using either "file" cards, or "cfile" |
| 223 | or "uvfile" cards. |
| 224 | The name "file" card comes from the fact that most artifacts correspond to |
| 225 | files that are under version control. |
| 226 | The "cfile" name is an abbreviation for "compressed file". |
| 227 | The "uvfile" name is an abbreviation for "unversioned file". |
| 228 | </p> |
| 229 | |
| 230 | <h4>3.3.1 Ordinary File Cards</h4> |
| 231 | |
| 232 | <p>For sync protocols, artifacts are transferred using "file" |
| 233 | cards. File cards come in two different formats depending |
| 234 | on whether the artifact is sent directly or as a delta from some |
| 235 | other artifact.</p> |
| 236 | |
| 237 | <blockquote> |
| 238 | <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br> |
| 239 | <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> |
| 240 | </blockquote> |
| 241 | |
| 242 | <p>File cards are followed by in-line "payload" data. |
| 243 | The content of the artifact |
| 244 | or the artifact delta is the first <i>size</i> bytes of the |
| 245 | x-fossil content that immediately follow the newline that |
| 246 | terminates the file card. |
| 247 | </p> |
| 248 | |
| 249 | <p>The first argument of a file card is the ID of the artifact that |
| 250 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 251 | representation of the name hash for the artifact. |
| 252 | The last argument of the file card is the number of bytes of |
| 253 | payload that immediately follow the file card. If the file |
| 254 | card has only two arguments, that means the payload is the |
| 255 | complete content of the artifact. If the file card has three |
| 256 | arguments, then the payload is a delta and second argument is |
| 257 | the ID of another artifact that is the source of the delta.</p> |
| 258 | |
| 259 | <p>File cards are sent in both directions: client to server and |
| 260 | server to client. A delta might be sent before the source of |
| 261 | the delta, so both client and server should remember deltas |
| 262 | and be able to apply them when their source arrives.</p> |
| 263 | |
| 264 | <h4>3.3.2 Compressed File Cards</h4> |
| 265 | |
| 266 | <p>A client that sends a clone protocol version "3" or greater will |
| 267 | receive artifacts as "cfile" cards while cloning. This card was |
| 268 | introduced to improve the speed of the transfer of content by sending the |
| 269 | compressed artifact directly from the server database to the client.</p> |
| 270 | |
| 271 | <p>Compressed File cards are similar to File cards, sharing the same |
| 272 | in-line "payload" data characteristics and also the same treatment of |
| 273 | direct content or delta content. Cfile cards come in two different formats |
| 274 | depending on whether the artifact is sent directly or as a delta from |
| 275 | some other artifact.</p> |
| 276 | |
| 277 | <blockquote> |
| 278 | <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 279 | <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 280 | </blockquote> |
| 281 | |
| 282 | <p>The first argument of the cfile card is the ID of the artifact that |
| 283 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 284 | representation of the name hash for the artifact. The second argument of |
| 285 | the cfile card is the original size in bytes of the artifact. The last |
| 286 | argument of the cfile card is the number of compressed bytes of payload |
| 287 | that immediately follow the cfile card. If the cfile card has only |
| 288 | three arguments, that means the payload is the complete content of the |
| 289 | artifact. If the cfile card has four arguments, then the payload is a |
| 290 | delta and the second argument is the ID of another artifact that is the |
| 291 | source of the delta and the third argument is the original size of the |
| 292 | delta artifact.</p> |
| 293 | |
| 294 | <p>Unlike file cards, cfile cards are only sent in one direction during a |
| 295 | clone from server to client for clone protocol version "3" or greater.</p> |
| 296 | |
| 297 | <h4>3.3.3 Private artifacts</h4> |
| 298 | |
| 299 | <p>"Private" content consist of artifacts that are not normally synced. |
| 300 | However, private content will be synced when the |
| 301 | the [/help?cmd=sync|fossil sync] command includes the "--private" option. |
| 302 | </p> |
| 303 | |
| 304 | <p>Private content is marked by a "private" card: |
| 305 | |
| 306 | <blockquote> |
| 307 | <b>private</b> |
| 308 | </blockquote> |
| 309 | |
| 310 | <p>The private card has no arguments and must directly precede a |
| 311 | file card that contains the private content.</p> |
| 312 | |
| 313 | <h4>3.3.4 Unversioned File Cards</h4> |
| 314 | |
| 315 | <p>Unversioned content is sent in both directions (client to server and |
| 316 | server to client) using "uvfile" cards in the following format: |
| 317 | |
| 318 | <blockquote> |
| 319 | <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i> |
| 320 | </blockquote> |
| 321 | |
| 322 | <p>The <i>name</i> field is the name of the unversioned file. The |
| 323 | <i>mtime</i> is the last modification time of the file in seconds |
| 324 | since 1970. The <i>hash</i> field is the hash of the content |
| 325 | for the unversioned file, or "<b>-</b>" for deleted content. |
| 326 | The <i>size</i> field is the (uncompressed) size of the content |
| 327 | in bytes. The <i>flags</i> field is an integer which is interpreted |
| @@ -329,234 +329,234 @@ | |
| 329 | the <i>content</i> is to be omitted. The content might be omitted if |
| 330 | it is too large to transmit, or if the sender merely wants to update the |
| 331 | modification time of the file without changing the files content. |
| 332 | The <i>content</i> is the (uncompressed) content of the file. |
| 333 | |
| 334 | <p>The receiver should only accept the uvfile card if the hash and |
| 335 | size match the content and if the mtime is newer than any existing |
| 336 | instance of the same file held by the receiver. The sender will not |
| 337 | normally transmit a uvfile card unless all these constraints are true, |
| 338 | but the receiver should double-check. |
| 339 | |
| 340 | <p>A server should only accept uvfile cards if the login user has |
| 341 | the "y" write-unversioned permission. |
| 342 | |
| 343 | <p>Servers send uvfile cards in response to uvgimme cards received from |
| 344 | the client. Clients send uvfile cards when they determine that the server |
| 345 | needs the content based on uvigot cards previously received from the server. |
| 346 | |
| 347 | <h3>3.4 Push and Pull Cards</h3> |
| 348 | |
| 349 | <p>Among the first cards in a client-to-server message are |
| 350 | the push and pull cards. The push card tells the server that |
| 351 | the client is pushing content. The pull card tells the server |
| 352 | that the client wants to pull content. In the event of a sync, |
| 353 | both cards are sent. The format is as follows:</p> |
| 354 | |
| 355 | <blockquote> |
| 356 | <b>push</b> <i>servercode projectcode</i><br> |
| 357 | <b>pull</b> <i>servercode projectcode</i> |
| 358 | </blockquote> |
| 359 | |
| 360 | <p>The <i>servercode</i> argument is the repository ID for the |
| 361 | client. The <i>projectcode</i> is the identifier |
| 362 | of the software project that the client repository contains. |
| 363 | The projectcode for the client and server must match in order |
| 364 | for the transaction to proceed.</p> |
| 365 | |
| 366 | <p>The server will also send a push card back to the client |
| 367 | during a clone. This is how the client determines what project |
| 368 | code to put in the new repository it is constructing.</p> |
| 369 | |
| 370 | <p>The <i>servercode</i> argument is currently unused. |
| 371 | |
| 372 | <h3>3.5 Clone Cards</h3> |
| 373 | |
| 374 | <p>A clone card works like a pull card in that it is sent from |
| 375 | client to server in order to tell the server that the client |
| 376 | wants to pull content. The clone card comes in two formats. Older |
| 377 | clients use the no-argument format and newer clients use the |
| 378 | two-argument format.</p> |
| 379 | |
| 380 | <blockquote> |
| 381 | <b>clone</b><br> |
| 382 | <b>clone</b> <i>protocol-version sequence-number</i> |
| 383 | </blockquote> |
| 384 | |
| 385 | <h4>3.5.1 Protocol 3</h4> |
| 386 | |
| 387 | <p>The latest clients send a two-argument clone message with a |
| 388 | protocol version of "3". (Future versions of Fossil might use larger |
| 389 | protocol version numbers.) Version "3" of the protocol enhanced version |
| 390 | "2" by introducing the "cfile" card which is intended to speed up clone |
| 391 | operations. Instead of sending "file" cards, the server will send "cfile" |
| 392 | cards</p> |
| 393 | |
| 394 | <h4>3.5.2 Protocol 2</h4> |
| 395 | |
| 396 | <p>The sequence-number sent is the number |
| 397 | of artifacts received so far. For the first clone message, the |
| 398 | sequence number is 0. The server will respond by sending file |
| 399 | cards for some number of artifacts up to the maximum message size. |
| 400 | |
| 401 | <p>The server will also send a single "clone_seqno" card to the client |
| 402 | so that the client can know where the server left off. |
| 403 | |
| 404 | <blockquote> |
| 405 | <b>clone_seqno</b> <i>sequence-number</i> |
| 406 | </blockquote> |
| 407 | |
| 408 | <p>The clone message in subsequent HTTP requests for the same clone |
| 409 | operation will use the sequence-number from the |
| 410 | clone_seqno of the previous reply.</p> |
| 411 | |
| 412 | <p>In response to an initial clone message, the server also sends the client |
| 413 | a push message so that the client can discover the projectcode for |
| 414 | this project.</p> |
| 415 | |
| 416 | <h4>3.5.3 Legacy Protocol</h4> |
| 417 | |
| 418 | <p>Older clients send a clone card with no argument. The server responds |
| 419 | to a blank clone card by sending an "igot" card for every artifact in the |
| 420 | repository. The client will then issue "gimme" cards to pull down all the |
| 421 | content it needs. |
| 422 | |
| 423 | <p>The legacy protocol works well for smaller repositories (50MB with 50,000 |
| 424 | artifacts) but is too slow and unwieldy for larger repositories. |
| 425 | The version 2 protocol is an effort to improve performance. Further |
| 426 | performance improvements with higher-numbered clone protocols are |
| 427 | possible in future versions of Fossil. |
| 428 | |
| 429 | <h3>3.6 Igot Cards</h3> |
| 430 | |
| 431 | <p>An igot card can be sent from either client to server or from |
| 432 | server to client in order to indicate that the sender holds a copy |
| 433 | of a particular artifact. The format is:</p> |
| 434 | |
| 435 | <blockquote> |
| 436 | <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? |
| 437 | </blockquote> |
| 438 | |
| 439 | <p>The first argument of the igot card is the ID of the artifact that |
| 440 | the sender possesses. |
| 441 | The receiver of an igot card will typically check to see if |
| 442 | it also holds the same artifact and if not it will request the artifact |
| 443 | using a gimme card in either the reply or in the next message.</p> |
| 444 | |
| 445 | <p>If the second argument exists and is "1", then the artifact |
| 446 | identified by the first argument is private on the sender and should |
| 447 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 448 | |
| 449 | <p>The name "igot" comes from the English slang expression "I got" meaning |
| 450 | "I have". |
| 451 | |
| 452 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 453 | |
| 454 | <p>Zero or more "uvigot" cards are sent from server to client when |
| 455 | synchronizing unversioned content. The format of a uvigot card is |
| 456 | as follows: |
| 457 | |
| 458 | <blockquote> |
| 459 | <b>uvigot</b> <i>name mtime hash size</i> |
| 460 | </blockquote> |
| 461 | |
| 462 | <p>The <i>name</i> argument is the name of an unversioned file. |
| 463 | The <i>mtime</i> is the last modification time of the unversioned file |
| 464 | in seconds since 1970. |
| 465 | The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file |
| 466 | content, or "<b>-</b>" if the file has been deleted. |
| 467 | The <i>size</i> is the uncompressed size of the file in bytes. |
| 468 | |
| 469 | <p>When the server sees a "pragma uv-hash" card for which the hash |
| 470 | does not match, it sends uvigot cards for every unversioned file that it |
| 471 | holds. The client will use this information to figure out which |
| 472 | unversioned files need to be synchronized. |
| 473 | The server might also send a uvigot card when it receives a uvgimme card |
| 474 | but its reply message size is already oversized and hence unable to hold |
| 475 | the usual uvfile reply. |
| 476 | |
| 477 | <p>When a client receives a "uvigot" card, it checks to see if the |
| 478 | file needs to be transferred from client to server or from server to client. |
| 479 | If a client-to-server transmission is needed, the client schedules that |
| 480 | transfer to occur on a subsequent HTTP request. If a server-to-client |
| 481 | transfer is needed, then the client sends a "uvgimme" card back to the |
| 482 | server to request the file content. |
| 483 | |
| 484 | <h3>3.7 Gimme Cards</h3> |
| 485 | |
| 486 | <p>A gimme card is sent from either client to server or from server |
| 487 | to client. The gimme card asks the receiver to send a particular |
| 488 | artifact back to the sender. The format of a gimme card is this:</p> |
| 489 | |
| 490 | <blockquote> |
| 491 | <b>gimme</b> <i>artifact-id</i> |
| 492 | </blockquote> |
| 493 | |
| 494 | <p>The argument to the gimme card is the ID of the artifact that |
| 495 | the sender wants. The receiver will typically respond to a |
| 496 | gimme card by sending a file card in its reply or in the next |
| 497 | message.</p> |
| 498 | |
| 499 | <p>The "gimme" name means "give me". The imperative "give me" is |
| 500 | pronounced as if it were a single word "gimme" in some dialects of |
| 501 | English (including the dialect spoken by the original author of Fossil). |
| 502 | |
| 503 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 504 | |
| 505 | <p>Sync synchronizing unversioned content, the client may send "uvgimme" |
| 506 | cards to the server. A uvgimme card requests that the server send |
| 507 | unversioned content to the client. The format of a uvgimme card is |
| 508 | as follows: |
| 509 | |
| 510 | <blockquote> |
| 511 | <b>uvgimme</b> <i>name</i> |
| 512 | </blockquote> |
| 513 | |
| 514 | <p>The <i>name</i> is the name of the unversioned file found on the |
| 515 | server that the client would like to have. When a server sees a |
| 516 | uvgimme card, it normally responses with a uvfile card, though it might |
| 517 | also send another uvigot card if the HTTP reply is already oversized. |
| 518 | |
| 519 | <h3>3.8 Cookie Cards</h3> |
| 520 | |
| 521 | <p>A cookie card can be used by a server to record a small amount |
| 522 | of state information on a client. The server sends a cookie to the |
| 523 | client. The client sends the same cookie back to the server on |
| 524 | its next request. The cookie card has a single argument which |
| 525 | is its payload.</p> |
| 526 | |
| 527 | <blockquote> |
| 528 | <b>cookie</b> <i>payload</i> |
| 529 | </blockquote> |
| 530 | |
| 531 | <p>The client is not required to return the cookie to the server on |
| 532 | its next request. Or the client might send a cookie from a different |
| 533 | server on the next request. So the server must not depend on the |
| 534 | cookie and the server must structure the cookie payload in such |
| 535 | a way that it can tell if the cookie it sees is its own cookie or |
| 536 | a cookie from another server. (Typically the server will embed |
| 537 | its servercode as part of the cookie.)</p> |
| 538 | |
| 539 | <h3>3.9 Request-Configuration Cards</h3> |
| 540 | |
| 541 | <p>A request-configuration or "reqconfig" card is sent from client to |
| 542 | server in order to request that the server send back "configuration" |
| 543 | data. "Configuration" data is information about users or website |
| 544 | appearance or other administrative details which are not part of the |
| 545 | persistent and versioned state of the project. For example, the "name" |
| 546 | of the project, the default Cascading Style Sheet (CSS) for the web-interface, |
| 547 | and the project logo displayed on the web-interface are all configuration |
| 548 | data elements. |
| 549 | |
| 550 | <p>The reqconfig card is normally sent in response to the |
| 551 | "fossil configuration pull" command. The format is as follows: |
| 552 | |
| 553 | <blockquote> |
| 554 | <b>reqconfig</b> <i>configuration-name</i> |
| 555 | </blockquote> |
| 556 | |
| 557 | <p>As of 2018-06-04, the configuration-name must be one of the |
| 558 | following values: |
| 559 | |
| 560 | <table border=0 align="center"> |
| 561 | <tr><td valign="top"> |
| 562 | <ul> |
| @@ -622,84 +622,81 @@ | |
| 622 | <li> @concealed |
| 623 | <li> @shun |
| 624 | </ul></td></tr> |
| 625 | </table> |
| 626 | |
| 627 | <p>New configuration-names are likely to be added in future releases of |
| 628 | Fossil. If the server receives a configuration-name that it does not |
| 629 | understand, the entire reqconfig card is silently ignored. The reqconfig |
| 630 | card might also be ignored if the user lacks sufficient privilege to |
| 631 | access the requested information. |
| 632 | |
| 633 | <p>The configuration-names that begin with an alphabetic character refer |
| 634 | to values in the "config" table of the server database. For example, |
| 635 | the "logo-image" configuration item refers to the project logo image |
| 636 | that is configured on the Admin page of the [./webui.wiki | web-interface]. |
| 637 | The value of the configuration item is returned to the client using a |
| 638 | "config" card. |
| 639 | |
| 640 | <p>If the configuration-name begins with "@", that refers to a class of |
| 641 | values instead of a single value. The content of these configuration items |
| 642 | is returned in a "config" card that contains pure SQL text that is |
| 643 | intended to be evaluated by the client. |
| 644 | |
| 645 | <p>The @user and @concealed configuration items contain sensitive information |
| 646 | and are ignored for clients without sufficient privilege. |
| 647 | |
| 648 | <h3>3.10 Configuration Cards</h3> |
| 649 | |
| 650 | <p>A "config" card is used to send configuration information from client |
| 651 | to server (in response to a "fossil configuration push" command) or |
| 652 | from server to client (in response to a "fossil configuration pull" or |
| 653 | "fossil clone" command). The format is as follows: |
| 654 | |
| 655 | <blockquote> |
| 656 | <b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i> |
| 657 | </blockquote> |
| 658 | |
| 659 | <p>The server will only accept a config card if the user has |
| 660 | "Admin" privilege. A client will only accept a config card if |
| 661 | it had sent a corresponding reqconfig card in its request. |
| 662 | |
| 663 | <p>The content of the configuration item is used to overwrite the |
| 664 | corresponding configuration data in the receiver. |
| 665 | |
| 666 | <h3>3.11 Pragma Cards</h3> |
| 667 | |
| 668 | <p>The client may try to influence the behavior of the server by |
| 669 | issuing a pragma card: |
| 670 | |
| 671 | <blockquote> |
| 672 | <b>pragma</i> <i>name value...</i> |
| 673 | </blockquote> |
| 674 | |
| 675 | <p>The "pragma" card has at least one argument which is the pragma name. |
| 676 | The pragma name defines what the pragma does. |
| 677 | A pragma might have zero or more "value" arguments |
| 678 | depending on the pragma name. |
| 679 | |
| 680 | <p>New pragma names may be added to the protocol from time to time |
| 681 | in order to enhance the capabilities of Fossil. |
| 682 | Unknown pragmas are silently ignored, for backwards compatibility. |
| 683 | |
| 684 | <p>The following are the known pragma names as of 2019-06-30: |
| 685 | |
| 686 | <ol> |
| 687 | <li><p><b>send-private</b> |
| 688 | <p>The send-private pragma instructs the server to send all of its |
| 689 | private artifacts to the client. The server will only obey this |
| 690 | request if the user has the "x" or "Private" privilege. |
| 691 | |
| 692 | <li><p><b>send-catalog</b> |
| 693 | <p>The send-catalog pragma instructs the server to transmit igot |
| 694 | cards for every known artifact. This can help the client and server |
| 695 | to get back in synchronization after a prior protocol error. The |
| 696 | "--verily" option to the [/help?cmd=sync|fossil sync] command causes |
| 697 | the send-catalog pragma to be transmitted.</p> |
| 698 | |
| 699 | <li><p><b>uv-hash</b> <i>HASH</i> |
| 700 | <p>The uv-hash pragma is sent from client to server to provoke a |
| 701 | synchronization of unversioned content. The <i>HASH</i> is a SHA1 |
| 702 | hash of the names, modification times, and individual hashes of all |
| 703 | unversioned files on the client. If the unversioned content hash |
| 704 | from the client does not match the unversioned content hash on the |
| 705 | server, then the server will reply with either a "pragma uv-push-ok" |
| @@ -707,157 +704,151 @@ | |
| 707 | each unversioned file currently held on the server. The collection |
| 708 | of "uvigot" cards sent in response to a "uv-hash" pragma is called |
| 709 | the "unversioned catalog". The client will used the unversioned |
| 710 | catalog to figure out which files (if any) need to be synchronized |
| 711 | between client and server and send appropriate "uvfile" or "uvgimme" |
| 712 | cards on the next HTTP request.</p> |
| 713 | |
| 714 | <p>If a client sends a uv-hash pragma and does not receive back |
| 715 | either a uv-pull-only or uv-push-ok pragma, that means that the |
| 716 | content on the server exactly matches the content on the client and |
| 717 | no further synchronization is required. |
| 718 | |
| 719 | <li><p><b>uv-pull-only</b></i> |
| 720 | <p>A server sends the uv-pull-only pragma to the client in response |
| 721 | to a uv-hash pragma with a mismatched content hash argument. This |
| 722 | pragma indicates that there are differences in unversioned content |
| 723 | between the client and server but that content can only be transferred |
| 724 | from server to client. The server is unwilling to accept content from |
| 725 | the client because the client login lacks the "write-unversioned" |
| 726 | permission.</p> |
| 727 | |
| 728 | <li><p><b>uv-push-ok</b></i> |
| 729 | <p>A server sends the uv-push-ok pragma to the client in response |
| 730 | to a uv-hash pragma with a mismatched content hash argument. This |
| 731 | pragma indicates that there are differences in unversioned content |
| 732 | between the client and server and that content can be transferred |
| 733 | in either direction. The server is willing to accept content from |
| 734 | the client because the client login has the "write-unversioned" |
| 735 | permission.</p> |
| 736 | |
| 737 | <li><p><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i></p> |
| 738 | <p>A client sends the "ci-lock" pragma to the server to indicate |
| 739 | that it is about to add a new check-in as a child of the |
| 740 | CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH. |
| 741 | If some other client has already indicated that it was also |
| 742 | trying to commit against CHECKIN-HASH, that indicates that a |
| 743 | fork is about to occur, and the server will reply with |
| 744 | a "ci-lock-fail" pragma (see below). Check-in locks |
| 745 | automatically expire when the check-in actually occurs, or |
| 746 | after a timeout (currently one minute but subject to change). |
| 747 | |
| 748 | <li><p><b>ci-lock-fail</b> <i>LOGIN MTIME</i></p> |
| 749 | <p>When a server receives two or more "ci-lock" pragma messages |
| 750 | for the same check-in but from different clients, the second a |
| 751 | subsequent ci-lock will provoke a ci-lock-fail pragma in the |
| 752 | reply to let the client know that it if continues with the |
| 753 | check-in it will likely generate a fork. The LOGIN and MTIME |
| 754 | arguments are intended to provide information to the client to |
| 755 | help it generate a more useful error message. |
| 756 | </p> |
| 757 | |
| 758 | <li><p><b>ci-unlock</b> <i>CLIENT-ID</i></p> |
| 759 | <p>A client sends the "ci-unlock" pragma to the server after |
| 760 | a successful commit. This instructs the server to release |
| 761 | any lock on any check-in previously held by that client. |
| 762 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 763 | that might arise if a check-in is aborted and then restarted |
| 764 | on a branch. |
| 765 | </ol> |
| 766 | |
| 767 | <h3>3.12 Comment Cards</h3> |
| 768 | |
| 769 | <p>Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 770 | is silently ignored.</p> |
| 771 | |
| 772 | <h3>3.13 Message and Error Cards</h3> |
| 773 | |
| 774 | <p>If the server discovers anything wrong with a request, it generates |
| 775 | an error card in its reply. When the client sees the error card, |
| 776 | it displays an error message to the user and aborts the sync |
| 777 | operation. An error card looks like this:</p> |
| 778 | |
| 779 | <blockquote> |
| 780 | <b>error</b> <i>error-message</i> |
| 781 | </blockquote> |
| 782 | |
| 783 | <p>The error message is English text that is encoded in order to |
| 784 | be a single token. |
| 785 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 786 | newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash |
| 787 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 788 | space and newline, no other whitespace characters nor any |
| 789 | unprintable characters are allowed in |
| 790 | the error message.</p> |
| 791 | |
| 792 | <p>The server can also send a message card that also prints a |
| 793 | message on the client console, but which is not an error: |
| 794 | |
| 795 | <blockquote> |
| 796 | <b>message</b> <i>message-text</i> |
| 797 | </blockquote> |
| 798 | |
| 799 | <p>The message-text uses the same format as an error message. |
| 800 | |
| 801 | <h3>3.14 Unknown Cards</h3> |
| 802 | |
| 803 | <p>If either the client or the server sees a card that is not |
| 804 | described above, then it generates an error and aborts.</p> |
| 805 | |
| 806 | <h2>4.0 Phantoms And Clusters</h2> |
| 807 | |
| 808 | <p>When a repository knows that an artifact exists and knows the ID of |
| 809 | that artifact, but it does not know the artifact content, then it stores that |
| 810 | artifact as a "phantom". A repository will typically create a phantom when |
| 811 | it receives an igot card for an artifact that it does not hold or when it |
| 812 | receives a file card that references a delta source that it does not |
| 813 | hold. When a server is generating its reply or when a client is |
| 814 | generating a new request, it will usually send gimme cards for every |
| 815 | phantom that it holds.</p> |
| 816 | |
| 817 | <p>A cluster is a special artifact that tells of the existence of other |
| 818 | artifacts. Any artifact in the repository that follows the syntactic rules |
| 819 | of a cluster is considered a cluster.</p> |
| 820 | |
| 821 | <p>A cluster is line oriented. Each line of a cluster |
| 822 | is a card. The cards are separated by the newline ("\n") character. |
| 823 | Each card consists of a single character card type, a space, and a |
| 824 | single argument. No extra whitespace and no trailing or leading |
| 825 | whitespace is allowed. All cards in the cluster must occur in |
| 826 | strict lexicographical order.</p> |
| 827 | |
| 828 | <p>A cluster consists of one or more "M" cards followed by a single |
| 829 | "Z" card. Each M card holds an argument which is an artifact ID for an |
| 830 | artifact in the repository. The Z card has a single argument which is the |
| 831 | lower-case hexadecimal representation of the MD5 checksum of all |
| 832 | preceding M cards up to and included the newline character that |
| 833 | occurred just before the Z that starts the Z card.</p> |
| 834 | |
| 835 | <p>Any artifact that does not match the specifications of a cluster |
| 836 | exactly is not a cluster. There must be no extra whitespace in |
| 837 | the artifact. There must be one or more M cards. There must be a |
| 838 | single Z card with a correct MD5 checksum. And all cards must |
| 839 | be in strict lexicographical order.</p> |
| 840 | |
| 841 | <h3>4.1 The Unclustered Table</h3> |
| 842 | |
| 843 | <p>Every repository maintains a table named "<b>unclustered</b>" |
| 844 | which records the identity of every artifact and phantom it holds that is not |
| 845 | mentioned in a cluster. The entries in the unclustered table can |
| 846 | be thought of as leaves on a tree of artifacts. Some of the unclustered |
| 847 | artifacts will be other clusters. Those clusters may contain other clusters, |
| 848 | which might contain still more clusters, and so forth. Beginning |
| 849 | with the artifacts in the unclustered table, one can follow the chain |
| 850 | of clusters to find every artifact in the repository.</p> |
| 851 | |
| 852 | <h2>5.0 Synchronization Strategies</h2> |
| 853 | |
| 854 | <h3>5.1 Pull</h3> |
| 855 | |
| 856 | <p>A typical pull operation proceeds as shown below. Details |
| 857 | of the actual implementation may very slightly but the gist of |
| 858 | a pull is captured in the following steps:</p> |
| 859 | |
| 860 | <ol> |
| 861 | <li>The client sends login and pull cards. |
| 862 | <li>The client sends a cookie card if it has previously received a cookie. |
| 863 | <li>The client sends gimme cards for every phantom that it holds. |
| @@ -877,38 +868,38 @@ | |
| 877 | that mentions an artifact that the client does not possess. |
| 878 | <li>The client creates a phantom for the delta source of file cards when |
| 879 | the delta source is an artifact that the client does not possess. |
| 880 | </ol> |
| 881 | |
| 882 | <p>These ten steps represent a single HTTP round-trip request. |
| 883 | The first three steps are the processing that occurs on the client |
| 884 | to generate the request. The middle four steps are processing |
| 885 | that occurs on the server to interpret the request and generate a |
| 886 | reply. And the last three steps are the processing that the |
| 887 | client does to interpret the reply.</p> |
| 888 | |
| 889 | <p>During a pull, the client will keep sending HTTP requests |
| 890 | until it holds all artifacts that exist on the server.</p> |
| 891 | |
| 892 | <p>Note that the server tries |
| 893 | to limit the size of its reply message to something reasonable |
| 894 | (usually about 1MB) so that it might stop sending file cards as |
| 895 | described in step (6) if the reply becomes too large.</p> |
| 896 | |
| 897 | <p>Step (5) is the only way in which new clusters can be created. |
| 898 | By only creating clusters on the server, we hope to minimize the |
| 899 | amount of overlap between clusters in the common configuration where |
| 900 | there is a single server and many clients. The same synchronization |
| 901 | protocol will continue to work even if there are multiple servers |
| 902 | or if servers and clients sometimes change roles. The only negative |
| 903 | effects of these unusual arrangements is that more than the minimum |
| 904 | number of clusters might be generated.</p> |
| 905 | |
| 906 | <h3>5.2 Push</h3> |
| 907 | |
| 908 | <p>A typical push operation proceeds roughly as shown below. As |
| 909 | with a pull, the actual implementation may vary slightly.</p> |
| 910 | |
| 911 | <ol> |
| 912 | <li>The client sends login and push cards. |
| 913 | <li>The client sends file cards for any artifacts that it holds that have |
| 914 | never before been pushed - artifacts that come from local check-ins. |
| @@ -929,40 +920,40 @@ | |
| 929 | <hr> |
| 930 | <li>The client remembers the gimme cards from the server so that it |
| 931 | can generate file cards in reply on the next cycle. |
| 932 | </ol> |
| 933 | |
| 934 | <p>As with a pull, the steps of a push operation repeat until the |
| 935 | server knows all artifacts that exist on the client. Also, as with |
| 936 | pull, the client attempts to keep the size of the request from |
| 937 | growing too large by suppressing file cards once the |
| 938 | size of the request reaches 1MB.</p> |
| 939 | |
| 940 | <h3 id="sync">5.3 Sync</h3> |
| 941 | |
| 942 | <p>A sync is just a pull and a push that happen at the same time. |
| 943 | The first three steps of a pull are combined with the first five steps |
| 944 | of a push. Steps (4) through (7) of a pull are combined with steps |
| 945 | (5) through (8) of a push. And steps (8) through (10) of a pull |
| 946 | are combined with step (9) of a push.</p> |
| 947 | |
| 948 | <h3>5.4 Unversioned File Sync</h3> |
| 949 | |
| 950 | <p>"Unversioned files" are files held in the repository |
| 951 | where only the most recent version of the file is kept rather than |
| 952 | the entire change history. Unversioned files are intended to be |
| 953 | used to store ephemeral content, such as compiled binaries of the |
| 954 | most recent release. |
| 955 | |
| 956 | <p>Unversioned files are identified by name and timestamp (mtime). |
| 957 | Only the most recent version of each file (the version with |
| 958 | the largest mtime value) is retained. |
| 959 | |
| 960 | <p>Unversioned files are synchronized using the |
| 961 | [/help?cmd=unversioned|fossil unversioned sync] command. |
| 962 | |
| 963 | <p>A schematic of an unversioned file synchronization is as follows: |
| 964 | |
| 965 | <ol> |
| 966 | <li>The client sends a "pragma uv-hash" card to the server. The argument |
| 967 | to the uv-hash pragma is a hash of all filesnames, mtimes, and |
| 968 | content hashes for the unversioned files held by the client. |
| @@ -981,17 +972,17 @@ | |
| 981 | <hr> |
| 982 | <li>The server updates its unversioned file store with received "uvfile" |
| 983 | cards and answers "uvgimme" cards with "uvfile" cards in its reply. |
| 984 | </ol> |
| 985 | |
| 986 | <p>The last two steps might be repeated multiple |
| 987 | times if there is more unversioned content to be transferred than will |
| 988 | fit comfortably in a single HTTP request. |
| 989 | |
| 990 | <h2>6.0 Summary</h2> |
| 991 | |
| 992 | <p>Here are the key points of the synchronization protocol:</p> |
| 993 | |
| 994 | <ol> |
| 995 | <li>The client sends one or more PUSH HTTP requests to the server. |
| 996 | The request and reply content type is "application/x-fossil". |
| 997 | <li>HTTP request content is compressed using zlib. |
| @@ -1031,11 +1022,11 @@ | |
| 1031 | gimme messages for those artifacts. |
| 1032 | </ol> |
| 1033 | |
| 1034 | <h2>7.0 Troubleshooting And Debugging Hints</h2> |
| 1035 | |
| 1036 | <p> |
| 1037 | If you run the [/help?cmd=sync|fossil sync] command |
| 1038 | (or [/help?cmd=pull|pull] or [/help?cmd=push|push] or |
| 1039 | [/help?cmd=clone|clone]) with the --httptrace option, Fossil |
| 1040 | will keep a copy of each HTTP request and reply in files |
| 1041 | named: |
| @@ -1042,17 +1033,17 @@ | |
| 1042 | <ul> |
| 1043 | <li> <tt>http-request-</tt><i>N</i><tt>.txt</tt> |
| 1044 | <li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt> |
| 1045 | </ul> |
| 1046 | |
| 1047 | <p>In the above, <i>N</i> is an integer that increments with each |
| 1048 | round-trip. If you are having trouble on the server side, |
| 1049 | you can run the "[/help?cmd=test-http|fossil test-http]" command in a |
| 1050 | debugger using one the "http-request-N.txt" files as input and |
| 1051 | single step through the processing performed by the server. |
| 1052 | |
| 1053 | <p>The "--transport-command CMD" option on [/help?cmd=sync|fossil sync] |
| 1054 | (and similar) causes the external program "CMD" to be used to move |
| 1055 | the sync message to the server and retrieve the sync reply. The |
| 1056 | CMD is given three arguments: |
| 1057 | <ol> |
| 1058 | <li> The URL of the server |
| @@ -1060,9 +1051,9 @@ | |
| 1060 | protocol text, with the HTTP headers |
| 1061 | <li> The name of a temporary file into which the CMD should write the |
| 1062 | reply sync protocol text, again without any HTTP headers |
| 1063 | </ol> |
| 1064 | |
| 1065 | <p>In a complex debugging situation, you can run the command |
| 1066 | "fossil sync --transport-command ./debugging_script" where |
| 1067 | "debugging_script" is some script of your own that invokes |
| 1068 | the anomolous behavior your are trying to debug. |
| 1069 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -1,13 +1,13 @@ | |
| 1 | <title>The Fossil Sync Protocol</title> |
| 2 | |
| 3 | This document describes the wire protocol used to synchronize |
| 4 | content between two Fossil repositories. |
| 5 | |
| 6 | <h2>1.0 Overview</h2> |
| 7 | |
| 8 | The global state of a fossil repository consists of an unordered |
| 9 | collection of artifacts. Each artifact is identified by a cryptographic |
| 10 | hash of its content, expressed as a lower-case hexadecimal string. |
| 11 | Synchronization is the process of sharing artifacts between |
| 12 | repositories so that all repositories have copies of all artifacts. Because |
| 13 | artifacts are unordered, the order in which artifacts are received |
| @@ -17,13 +17,13 @@ | |
| 17 | of hashes for available artifacts, then sharing the content of artifacts |
| 18 | whose names are missing from one side or the other of the connection. |
| 19 | In practice, a repository might contain millions of artifacts. The list of |
| 20 | hash names for this many artifacts can be large. So optimizations are |
| 21 | employed that usually reduce the number of hashes that need to be |
| 22 | shared to a few hundred. |
| 23 | |
| 24 | Each repository also has local state. The local state determines |
| 25 | the web-page formatting preferences, authorized users, ticket formats, |
| 26 | and similar information that varies from one repository to another. |
| 27 | The local state is not usually transferred during a sync. Except, |
| 28 | some local state is transferred during a [/help?cmd=clone|clone] |
| 29 | in order to initialize the local state of the new repository. Also, |
| @@ -32,67 +32,67 @@ | |
| 32 | [/help?cmd=configuration|config pull] |
| 33 | commands. |
| 34 | |
| 35 | <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> |
| 36 | |
| 37 | The "bag of artifacts" data model used by Fossil is apparently an |
| 38 | implementation of a particular |
| 39 | [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free |
| 40 | Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The |
| 41 | academic literature on CRDTs only began to appear in about 2011, and |
| 42 | Fossil predates that research by at least 4 years. But it is nice to |
| 43 | know that theorists have now proven that the underlying data model of |
| 44 | Fossil can provide strongly-consistent replicas using only |
| 45 | peer-to-peer communication and without any kind of central |
| 46 | authority. |
| 47 | |
| 48 | If you are already familiar with CRDTs and were wondering if Fossil |
| 49 | used them, the answer is "yes". We just don't call them by that name. |
| 50 | |
| 51 | |
| 52 | <h2>2.0 Transport</h2> |
| 53 | |
| 54 | All communication between client and server is via HTTP requests. |
| 55 | The server is listening for incoming HTTP requests. The client |
| 56 | issues one or more HTTP requests and receives replies for each |
| 57 | request. |
| 58 | |
| 59 | The server might be running as an independent server |
| 60 | using the <b>server</b> command, or it might be launched from |
| 61 | inetd or xinetd using the <b>http</b> command. Or the server might |
| 62 | be launched from CGI. |
| 63 | (See "[./server/|How To Configure A Fossil Server]" for details.) |
| 64 | The specifics of how the server listens |
| 65 | for incoming HTTP requests is immaterial to this protocol. |
| 66 | The important point is that the server is listening for requests and |
| 67 | the client is the issuer of the requests. |
| 68 | |
| 69 | A single push, pull, or sync might involve multiple HTTP requests. |
| 70 | The client maintains state between all requests. But on the server |
| 71 | side, each request is independent. The server does not preserve |
| 72 | any information about the client from one request to the next. |
| 73 | |
| 74 | Note: Throughout this article, we use the terms "server" and "client" |
| 75 | to represent the listener and initiator of the interaction, respectively. |
| 76 | Nothing in this protocol requires that the server actually be a back-room |
| 77 | processor housed in a datacenter, nor does the client need to be a desktop |
| 78 | or handheld device. For the purposes of this article "client" simply means |
| 79 | the repository that initiates the conversation and "server" is the repository |
| 80 | that responds. Nothing more. |
| 81 | |
| 82 | <h4>2.0.1 HTTPS Transport</h4> |
| 83 | |
| 84 | In the current implementation of Fossil, the server only |
| 85 | understands HTTP requests. The client can send either |
| 86 | clear-text HTTP requests or encrypted HTTPS requests. But when |
| 87 | HTTPS requests are sent, they first must be decrypted by a web server |
| 88 | or proxy before being passed to the Fossil server. This limitation |
| 89 | may be relaxed in a future release. |
| 90 | |
| 91 | <h4>2.0.2 SSH Transport</h4> |
| 92 | |
| 93 | When doing a sync using an "ssh:..." URL, the same HTTP transport protocol |
| 94 | is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh] |
| 95 | to start an instance of the [/help?cmd=test-http|fossil test-http] command |
| 96 | running on the remote machine. It then sends HTTP requests and gets back HTTP |
| 97 | replies over the SSH connection, rather than sending and receiving over an |
| 98 | internet socket. To see the specific "ssh" command that the Fossil client |
| @@ -99,11 +99,11 @@ | |
| 99 | runs in order to set up a connection, add either of the the "--httptrace" or |
| 100 | "--sshtrace" options to the "fossil sync" command line. |
| 101 | |
| 102 | <h4>2.0.3 FILE Transport</h4> |
| 103 | |
| 104 | When doing a sync using a "file:..." URL, the same HTTP protocol is |
| 105 | still used. But instead of sending each HTTP request over a socket or |
| 106 | via SSH, the HTTP request is written into a temporary file. The client |
| 107 | then invokes the [/help?cmd=http|fossil http] command in a subprocess |
| 108 | to process the request and and generate a reply. The client then reads |
| 109 | the HTTP reply out of a temporary file on disk, and deletes the two |
| @@ -111,36 +111,36 @@ | |
| 111 | in order to implement the "file:" transport, add the "--httptrace" |
| 112 | option to the "fossil sync" command. |
| 113 | |
| 114 | <h3>2.1 Server Identification</h3> |
| 115 | |
| 116 | The server is identified by a URL argument that accompanies the |
| 117 | push, pull, or sync command on the client. (As a convenience to |
| 118 | users, the URL can be omitted on the client command and the same URL |
| 119 | from the most recent push, pull, or sync will be reused. This saves |
| 120 | typing in the common case where the client does multiple syncs to |
| 121 | the same server.) |
| 122 | |
| 123 | The client modifies the URL by appending the method name "<b>/xfer</b>" |
| 124 | to the end. For example, if the URL specified on the client command |
| 125 | line is |
| 126 | |
| 127 | <blockquote> |
| 128 | https://fossil-scm.org/fossil |
| 129 | </blockquote> |
| 130 | |
| 131 | Then the URL that is really used to do the synchronization will |
| 132 | be: |
| 133 | |
| 134 | <blockquote> |
| 135 | https://fossil-scm.org/fossil/xfer |
| 136 | </blockquote> |
| 137 | |
| 138 | <h3>2.2 HTTP Request Format</h3> |
| 139 | |
| 140 | The client always sends a POST request to the server. The |
| 141 | general format of the POST request is as follows: |
| 142 | |
| 143 | <blockquote><pre> |
| 144 | POST /fossil/xfer HTTP/1.0 |
| 145 | Host: fossil-scm.hwaci.com:80 |
| 146 | Content-Type: application/x-fossil |
| @@ -147,19 +147,19 @@ | |
| 147 | Content-Length: 4216 |
| 148 | |
| 149 | <i>content...</i> |
| 150 | </pre></blockquote> |
| 151 | |
| 152 | In the example above, the pathname given after the POST keyword |
| 153 | on the first line is a copy of the URL pathname. The Host: parameter |
| 154 | is also taken from the URL. The content type is always either |
| 155 | "application/x-fossil" or "application/x-fossil-debug". The "x-fossil" |
| 156 | content type is the default. The only difference is that "x-fossil" |
| 157 | content is compressed using zlib whereas "x-fossil-debug" is sent |
| 158 | uncompressed. |
| 159 | |
| 160 | A typical reply from the server might look something like this: |
| 161 | |
| 162 | <blockquote><pre> |
| 163 | HTTP/1.0 200 OK |
| 164 | Date: Mon, 10 Sep 2007 12:21:01 GMT |
| 165 | Connection: close |
| @@ -168,160 +168,160 @@ | |
| 168 | Content-Length: 265 |
| 169 | |
| 170 | <i>content...</i> |
| 171 | </pre></blockquote> |
| 172 | |
| 173 | The content type of the reply is always the same as the content type |
| 174 | of the request. |
| 175 | |
| 176 | <h2>3.0 Fossil Synchronization Content</h2> |
| 177 | |
| 178 | A synchronization request between a client and server consists of |
| 179 | one or more HTTP requests as described in the previous section. This |
| 180 | section details the "x-fossil" content type. |
| 181 | |
| 182 | <h3>3.1 Line-oriented Format</h3> |
| 183 | |
| 184 | The x-fossil content type consists of zero or more "cards". Cards |
| 185 | are separated by the newline character ("\n"). Leading and trailing |
| 186 | whitespace on a card is ignored. Blank cards are ignored. |
| 187 | |
| 188 | Each card is divided into zero or more space separated tokens. |
| 189 | The first token on each card is the operator. Subsequent tokens |
| 190 | are arguments. The set of operators understood by servers is slightly |
| 191 | different from the operators understood by clients, though the two |
| 192 | are very similar. |
| 193 | |
| 194 | <h3>3.2 Login Cards</h3> |
| 195 | |
| 196 | Every message from client to server begins with one or more login |
| 197 | cards. Each login card has the following format: |
| 198 | |
| 199 | <blockquote> |
| 200 | <b>login</b> <i>userid nonce signature</i> |
| 201 | </blockquote> |
| 202 | |
| 203 | The userid is the name of the user that is requesting service |
| 204 | from the server. The nonce is the SHA1 hash of the remainder of |
| 205 | the message - all text that follows the newline character that |
| 206 | terminates the login card. The signature is the SHA1 hash of |
| 207 | the concatenation of the nonce and the users password. |
| 208 | |
| 209 | For each login card, the server looks up the user and verifies |
| 210 | that the nonce matches the SHA1 hash of the remainder of the |
| 211 | message. It then checks the signature hash to make sure the |
| 212 | signature matches. If everything |
| 213 | checks out, then the client is granted all privileges of the |
| 214 | specified user. |
| 215 | |
| 216 | Privileges are cumulative. There can be multiple successful |
| 217 | login cards. The session privileges are the bit-wise OR of the |
| 218 | privileges of each individual login. |
| 219 | |
| 220 | <h3>3.3 File Cards</h3> |
| 221 | |
| 222 | Artifacts are transferred using either "file" cards, or "cfile" |
| 223 | or "uvfile" cards. |
| 224 | The name "file" card comes from the fact that most artifacts correspond to |
| 225 | files that are under version control. |
| 226 | The "cfile" name is an abbreviation for "compressed file". |
| 227 | The "uvfile" name is an abbreviation for "unversioned file". |
| 228 | |
| 229 | |
| 230 | <h4>3.3.1 Ordinary File Cards</h4> |
| 231 | |
| 232 | For sync protocols, artifacts are transferred using "file" |
| 233 | cards. File cards come in two different formats depending |
| 234 | on whether the artifact is sent directly or as a delta from some |
| 235 | other artifact. |
| 236 | |
| 237 | <blockquote> |
| 238 | <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br> |
| 239 | <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> |
| 240 | </blockquote> |
| 241 | |
| 242 | File cards are followed by in-line "payload" data. |
| 243 | The content of the artifact |
| 244 | or the artifact delta is the first <i>size</i> bytes of the |
| 245 | x-fossil content that immediately follow the newline that |
| 246 | terminates the file card. |
| 247 | |
| 248 | |
| 249 | The first argument of a file card is the ID of the artifact that |
| 250 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 251 | representation of the name hash for the artifact. |
| 252 | The last argument of the file card is the number of bytes of |
| 253 | payload that immediately follow the file card. If the file |
| 254 | card has only two arguments, that means the payload is the |
| 255 | complete content of the artifact. If the file card has three |
| 256 | arguments, then the payload is a delta and second argument is |
| 257 | the ID of another artifact that is the source of the delta. |
| 258 | |
| 259 | File cards are sent in both directions: client to server and |
| 260 | server to client. A delta might be sent before the source of |
| 261 | the delta, so both client and server should remember deltas |
| 262 | and be able to apply them when their source arrives. |
| 263 | |
| 264 | <h4>3.3.2 Compressed File Cards</h4> |
| 265 | |
| 266 | A client that sends a clone protocol version "3" or greater will |
| 267 | receive artifacts as "cfile" cards while cloning. This card was |
| 268 | introduced to improve the speed of the transfer of content by sending the |
| 269 | compressed artifact directly from the server database to the client. |
| 270 | |
| 271 | Compressed File cards are similar to File cards, sharing the same |
| 272 | in-line "payload" data characteristics and also the same treatment of |
| 273 | direct content or delta content. Cfile cards come in two different formats |
| 274 | depending on whether the artifact is sent directly or as a delta from |
| 275 | some other artifact. |
| 276 | |
| 277 | <blockquote> |
| 278 | <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 279 | <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br> |
| 280 | </blockquote> |
| 281 | |
| 282 | The first argument of the cfile card is the ID of the artifact that |
| 283 | is being transferred. The artifact ID is the lower-case hexadecimal |
| 284 | representation of the name hash for the artifact. The second argument of |
| 285 | the cfile card is the original size in bytes of the artifact. The last |
| 286 | argument of the cfile card is the number of compressed bytes of payload |
| 287 | that immediately follow the cfile card. If the cfile card has only |
| 288 | three arguments, that means the payload is the complete content of the |
| 289 | artifact. If the cfile card has four arguments, then the payload is a |
| 290 | delta and the second argument is the ID of another artifact that is the |
| 291 | source of the delta and the third argument is the original size of the |
| 292 | delta artifact. |
| 293 | |
| 294 | Unlike file cards, cfile cards are only sent in one direction during a |
| 295 | clone from server to client for clone protocol version "3" or greater. |
| 296 | |
| 297 | <h4>3.3.3 Private artifacts</h4> |
| 298 | |
| 299 | "Private" content consist of artifacts that are not normally synced. |
| 300 | However, private content will be synced when the |
| 301 | the [/help?cmd=sync|fossil sync] command includes the "--private" option. |
| 302 | |
| 303 | |
| 304 | Private content is marked by a "private" card: |
| 305 | |
| 306 | <blockquote> |
| 307 | <b>private</b> |
| 308 | </blockquote> |
| 309 | |
| 310 | The private card has no arguments and must directly precede a |
| 311 | file card that contains the private content. |
| 312 | |
| 313 | <h4>3.3.4 Unversioned File Cards</h4> |
| 314 | |
| 315 | Unversioned content is sent in both directions (client to server and |
| 316 | server to client) using "uvfile" cards in the following format: |
| 317 | |
| 318 | <blockquote> |
| 319 | <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i> |
| 320 | </blockquote> |
| 321 | |
| 322 | The <i>name</i> field is the name of the unversioned file. The |
| 323 | <i>mtime</i> is the last modification time of the file in seconds |
| 324 | since 1970. The <i>hash</i> field is the hash of the content |
| 325 | for the unversioned file, or "<b>-</b>" for deleted content. |
| 326 | The <i>size</i> field is the (uncompressed) size of the content |
| 327 | in bytes. The <i>flags</i> field is an integer which is interpreted |
| @@ -329,234 +329,234 @@ | |
| 329 | the <i>content</i> is to be omitted. The content might be omitted if |
| 330 | it is too large to transmit, or if the sender merely wants to update the |
| 331 | modification time of the file without changing the files content. |
| 332 | The <i>content</i> is the (uncompressed) content of the file. |
| 333 | |
| 334 | The receiver should only accept the uvfile card if the hash and |
| 335 | size match the content and if the mtime is newer than any existing |
| 336 | instance of the same file held by the receiver. The sender will not |
| 337 | normally transmit a uvfile card unless all these constraints are true, |
| 338 | but the receiver should double-check. |
| 339 | |
| 340 | A server should only accept uvfile cards if the login user has |
| 341 | the "y" write-unversioned permission. |
| 342 | |
| 343 | Servers send uvfile cards in response to uvgimme cards received from |
| 344 | the client. Clients send uvfile cards when they determine that the server |
| 345 | needs the content based on uvigot cards previously received from the server. |
| 346 | |
| 347 | <h3>3.4 Push and Pull Cards</h3> |
| 348 | |
| 349 | Among the first cards in a client-to-server message are |
| 350 | the push and pull cards. The push card tells the server that |
| 351 | the client is pushing content. The pull card tells the server |
| 352 | that the client wants to pull content. In the event of a sync, |
| 353 | both cards are sent. The format is as follows: |
| 354 | |
| 355 | <blockquote> |
| 356 | <b>push</b> <i>servercode projectcode</i><br> |
| 357 | <b>pull</b> <i>servercode projectcode</i> |
| 358 | </blockquote> |
| 359 | |
| 360 | The <i>servercode</i> argument is the repository ID for the |
| 361 | client. The <i>projectcode</i> is the identifier |
| 362 | of the software project that the client repository contains. |
| 363 | The projectcode for the client and server must match in order |
| 364 | for the transaction to proceed. |
| 365 | |
| 366 | The server will also send a push card back to the client |
| 367 | during a clone. This is how the client determines what project |
| 368 | code to put in the new repository it is constructing. |
| 369 | |
| 370 | The <i>servercode</i> argument is currently unused. |
| 371 | |
| 372 | <h3>3.5 Clone Cards</h3> |
| 373 | |
| 374 | A clone card works like a pull card in that it is sent from |
| 375 | client to server in order to tell the server that the client |
| 376 | wants to pull content. The clone card comes in two formats. Older |
| 377 | clients use the no-argument format and newer clients use the |
| 378 | two-argument format. |
| 379 | |
| 380 | <blockquote> |
| 381 | <b>clone</b><br> |
| 382 | <b>clone</b> <i>protocol-version sequence-number</i> |
| 383 | </blockquote> |
| 384 | |
| 385 | <h4>3.5.1 Protocol 3</h4> |
| 386 | |
| 387 | The latest clients send a two-argument clone message with a |
| 388 | protocol version of "3". (Future versions of Fossil might use larger |
| 389 | protocol version numbers.) Version "3" of the protocol enhanced version |
| 390 | "2" by introducing the "cfile" card which is intended to speed up clone |
| 391 | operations. Instead of sending "file" cards, the server will send "cfile" |
| 392 | cards |
| 393 | |
| 394 | <h4>3.5.2 Protocol 2</h4> |
| 395 | |
| 396 | The sequence-number sent is the number |
| 397 | of artifacts received so far. For the first clone message, the |
| 398 | sequence number is 0. The server will respond by sending file |
| 399 | cards for some number of artifacts up to the maximum message size. |
| 400 | |
| 401 | The server will also send a single "clone_seqno" card to the client |
| 402 | so that the client can know where the server left off. |
| 403 | |
| 404 | <blockquote> |
| 405 | <b>clone_seqno</b> <i>sequence-number</i> |
| 406 | </blockquote> |
| 407 | |
| 408 | The clone message in subsequent HTTP requests for the same clone |
| 409 | operation will use the sequence-number from the |
| 410 | clone_seqno of the previous reply. |
| 411 | |
| 412 | In response to an initial clone message, the server also sends the client |
| 413 | a push message so that the client can discover the projectcode for |
| 414 | this project. |
| 415 | |
| 416 | <h4>3.5.3 Legacy Protocol</h4> |
| 417 | |
| 418 | Older clients send a clone card with no argument. The server responds |
| 419 | to a blank clone card by sending an "igot" card for every artifact in the |
| 420 | repository. The client will then issue "gimme" cards to pull down all the |
| 421 | content it needs. |
| 422 | |
| 423 | The legacy protocol works well for smaller repositories (50MB with 50,000 |
| 424 | artifacts) but is too slow and unwieldy for larger repositories. |
| 425 | The version 2 protocol is an effort to improve performance. Further |
| 426 | performance improvements with higher-numbered clone protocols are |
| 427 | possible in future versions of Fossil. |
| 428 | |
| 429 | <h3>3.6 Igot Cards</h3> |
| 430 | |
| 431 | An igot card can be sent from either client to server or from |
| 432 | server to client in order to indicate that the sender holds a copy |
| 433 | of a particular artifact. The format is: |
| 434 | |
| 435 | <blockquote> |
| 436 | <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? |
| 437 | </blockquote> |
| 438 | |
| 439 | The first argument of the igot card is the ID of the artifact that |
| 440 | the sender possesses. |
| 441 | The receiver of an igot card will typically check to see if |
| 442 | it also holds the same artifact and if not it will request the artifact |
| 443 | using a gimme card in either the reply or in the next message. |
| 444 | |
| 445 | If the second argument exists and is "1", then the artifact |
| 446 | identified by the first argument is private on the sender and should |
| 447 | be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. |
| 448 | |
| 449 | The name "igot" comes from the English slang expression "I got" meaning |
| 450 | "I have". |
| 451 | |
| 452 | <h4>3.6.1 Unversioned Igot Cards</h4> |
| 453 | |
| 454 | Zero or more "uvigot" cards are sent from server to client when |
| 455 | synchronizing unversioned content. The format of a uvigot card is |
| 456 | as follows: |
| 457 | |
| 458 | <blockquote> |
| 459 | <b>uvigot</b> <i>name mtime hash size</i> |
| 460 | </blockquote> |
| 461 | |
| 462 | The <i>name</i> argument is the name of an unversioned file. |
| 463 | The <i>mtime</i> is the last modification time of the unversioned file |
| 464 | in seconds since 1970. |
| 465 | The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file |
| 466 | content, or "<b>-</b>" if the file has been deleted. |
| 467 | The <i>size</i> is the uncompressed size of the file in bytes. |
| 468 | |
| 469 | When the server sees a "pragma uv-hash" card for which the hash |
| 470 | does not match, it sends uvigot cards for every unversioned file that it |
| 471 | holds. The client will use this information to figure out which |
| 472 | unversioned files need to be synchronized. |
| 473 | The server might also send a uvigot card when it receives a uvgimme card |
| 474 | but its reply message size is already oversized and hence unable to hold |
| 475 | the usual uvfile reply. |
| 476 | |
| 477 | When a client receives a "uvigot" card, it checks to see if the |
| 478 | file needs to be transferred from client to server or from server to client. |
| 479 | If a client-to-server transmission is needed, the client schedules that |
| 480 | transfer to occur on a subsequent HTTP request. If a server-to-client |
| 481 | transfer is needed, then the client sends a "uvgimme" card back to the |
| 482 | server to request the file content. |
| 483 | |
| 484 | <h3>3.7 Gimme Cards</h3> |
| 485 | |
| 486 | A gimme card is sent from either client to server or from server |
| 487 | to client. The gimme card asks the receiver to send a particular |
| 488 | artifact back to the sender. The format of a gimme card is this: |
| 489 | |
| 490 | <blockquote> |
| 491 | <b>gimme</b> <i>artifact-id</i> |
| 492 | </blockquote> |
| 493 | |
| 494 | The argument to the gimme card is the ID of the artifact that |
| 495 | the sender wants. The receiver will typically respond to a |
| 496 | gimme card by sending a file card in its reply or in the next |
| 497 | message. |
| 498 | |
| 499 | The "gimme" name means "give me". The imperative "give me" is |
| 500 | pronounced as if it were a single word "gimme" in some dialects of |
| 501 | English (including the dialect spoken by the original author of Fossil). |
| 502 | |
| 503 | <h4>3.7.1 Unversioned Gimme Cards</h4> |
| 504 | |
| 505 | Sync synchronizing unversioned content, the client may send "uvgimme" |
| 506 | cards to the server. A uvgimme card requests that the server send |
| 507 | unversioned content to the client. The format of a uvgimme card is |
| 508 | as follows: |
| 509 | |
| 510 | <blockquote> |
| 511 | <b>uvgimme</b> <i>name</i> |
| 512 | </blockquote> |
| 513 | |
| 514 | The <i>name</i> is the name of the unversioned file found on the |
| 515 | server that the client would like to have. When a server sees a |
| 516 | uvgimme card, it normally responses with a uvfile card, though it might |
| 517 | also send another uvigot card if the HTTP reply is already oversized. |
| 518 | |
| 519 | <h3>3.8 Cookie Cards</h3> |
| 520 | |
| 521 | A cookie card can be used by a server to record a small amount |
| 522 | of state information on a client. The server sends a cookie to the |
| 523 | client. The client sends the same cookie back to the server on |
| 524 | its next request. The cookie card has a single argument which |
| 525 | is its payload. |
| 526 | |
| 527 | <blockquote> |
| 528 | <b>cookie</b> <i>payload</i> |
| 529 | </blockquote> |
| 530 | |
| 531 | The client is not required to return the cookie to the server on |
| 532 | its next request. Or the client might send a cookie from a different |
| 533 | server on the next request. So the server must not depend on the |
| 534 | cookie and the server must structure the cookie payload in such |
| 535 | a way that it can tell if the cookie it sees is its own cookie or |
| 536 | a cookie from another server. (Typically the server will embed |
| 537 | its servercode as part of the cookie.) |
| 538 | |
| 539 | <h3>3.9 Request-Configuration Cards</h3> |
| 540 | |
| 541 | A request-configuration or "reqconfig" card is sent from client to |
| 542 | server in order to request that the server send back "configuration" |
| 543 | data. "Configuration" data is information about users or website |
| 544 | appearance or other administrative details which are not part of the |
| 545 | persistent and versioned state of the project. For example, the "name" |
| 546 | of the project, the default Cascading Style Sheet (CSS) for the web-interface, |
| 547 | and the project logo displayed on the web-interface are all configuration |
| 548 | data elements. |
| 549 | |
| 550 | The reqconfig card is normally sent in response to the |
| 551 | "fossil configuration pull" command. The format is as follows: |
| 552 | |
| 553 | <blockquote> |
| 554 | <b>reqconfig</b> <i>configuration-name</i> |
| 555 | </blockquote> |
| 556 | |
| 557 | As of 2018-06-04, the configuration-name must be one of the |
| 558 | following values: |
| 559 | |
| 560 | <table border=0 align="center"> |
| 561 | <tr><td valign="top"> |
| 562 | <ul> |
| @@ -622,84 +622,81 @@ | |
| 622 | <li> @concealed |
| 623 | <li> @shun |
| 624 | </ul></td></tr> |
| 625 | </table> |
| 626 | |
| 627 | New configuration-names are likely to be added in future releases of |
| 628 | Fossil. If the server receives a configuration-name that it does not |
| 629 | understand, the entire reqconfig card is silently ignored. The reqconfig |
| 630 | card might also be ignored if the user lacks sufficient privilege to |
| 631 | access the requested information. |
| 632 | |
| 633 | The configuration-names that begin with an alphabetic character refer |
| 634 | to values in the "config" table of the server database. For example, |
| 635 | the "logo-image" configuration item refers to the project logo image |
| 636 | that is configured on the Admin page of the [./webui.wiki | web-interface]. |
| 637 | The value of the configuration item is returned to the client using a |
| 638 | "config" card. |
| 639 | |
| 640 | If the configuration-name begins with "@", that refers to a class of |
| 641 | values instead of a single value. The content of these configuration items |
| 642 | is returned in a "config" card that contains pure SQL text that is |
| 643 | intended to be evaluated by the client. |
| 644 | |
| 645 | The @user and @concealed configuration items contain sensitive information |
| 646 | and are ignored for clients without sufficient privilege. |
| 647 | |
| 648 | <h3>3.10 Configuration Cards</h3> |
| 649 | |
| 650 | A "config" card is used to send configuration information from client |
| 651 | to server (in response to a "fossil configuration push" command) or |
| 652 | from server to client (in response to a "fossil configuration pull" or |
| 653 | "fossil clone" command). The format is as follows: |
| 654 | |
| 655 | <blockquote> |
| 656 | <b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i> |
| 657 | </blockquote> |
| 658 | |
| 659 | The server will only accept a config card if the user has |
| 660 | "Admin" privilege. A client will only accept a config card if |
| 661 | it had sent a corresponding reqconfig card in its request. |
| 662 | |
| 663 | The content of the configuration item is used to overwrite the |
| 664 | corresponding configuration data in the receiver. |
| 665 | |
| 666 | <h3>3.11 Pragma Cards</h3> |
| 667 | |
| 668 | The client may try to influence the behavior of the server by |
| 669 | issuing a pragma card: |
| 670 | |
| 671 | <blockquote> |
| 672 | <b>pragma</i> <i>name value...</i> |
| 673 | </blockquote> |
| 674 | |
| 675 | The "pragma" card has at least one argument which is the pragma name. |
| 676 | The pragma name defines what the pragma does. |
| 677 | A pragma might have zero or more "value" arguments |
| 678 | depending on the pragma name. |
| 679 | |
| 680 | New pragma names may be added to the protocol from time to time |
| 681 | in order to enhance the capabilities of Fossil. |
| 682 | Unknown pragmas are silently ignored, for backwards compatibility. |
| 683 | |
| 684 | The following are the known pragma names as of 2019-06-30: |
| 685 | |
| 686 | <ol> |
| 687 | <li><b>send-private</b> The send-private pragma instructs the server to send all of its |
| 688 | private artifacts to the client. The server will only obey this |
| 689 | request if the user has the "x" or "Private" privilege. |
| 690 | |
| 691 | <li><b>send-catalog</b> The send-catalog pragma instructs the server to transmit igot |
| 692 | cards for every known artifact. This can help the client and server |
| 693 | to get back in synchronization after a prior protocol error. The |
| 694 | "--verily" option to the [/help?cmd=sync|fossil sync] command causes |
| 695 | the send-catalog pragma to be transmitted. |
| 696 | |
| 697 | <li><b>uv-hash</b> <i>HASH</i> The uv-hash pragma is sent from client to server to provoke a |
| 698 | synchronization of unversioned content. The <i>HASH</i> is a SHA1 |
| 699 | hash of the names, modification times, and individual hashes of all |
| 700 | unversioned files on the client. If the unversioned content hash |
| 701 | from the client does not match the unversioned content hash on the |
| 702 | server, then the server will reply with either a "pragma uv-push-ok" |
| @@ -707,157 +704,151 @@ | |
| 704 | each unversioned file currently held on the server. The collection |
| 705 | of "uvigot" cards sent in response to a "uv-hash" pragma is called |
| 706 | the "unversioned catalog". The client will used the unversioned |
| 707 | catalog to figure out which files (if any) need to be synchronized |
| 708 | between client and server and send appropriate "uvfile" or "uvgimme" |
| 709 | cards on the next HTTP request. |
| 710 | |
| 711 | If a client sends a uv-hash pragma and does not receive back |
| 712 | either a uv-pull-only or uv-push-ok pragma, that means that the |
| 713 | content on the server exactly matches the content on the client and |
| 714 | no further synchronization is required. |
| 715 | |
| 716 | <li><b>uv-pull-only</b></i> A server sends the uv-pull-only pragma to the client in response |
| 717 | to a uv-hash pragma with a mismatched content hash argument. This |
| 718 | pragma indicates that there are differences in unversioned content |
| 719 | between the client and server but that content can only be transferred |
| 720 | from server to client. The server is unwilling to accept content from |
| 721 | the client because the client login lacks the "write-unversioned" |
| 722 | permission. |
| 723 | |
| 724 | <li><b>uv-push-ok</b></i> A server sends the uv-push-ok pragma to the client in response |
| 725 | to a uv-hash pragma with a mismatched content hash argument. This |
| 726 | pragma indicates that there are differences in unversioned content |
| 727 | between the client and server and that content can be transferred |
| 728 | in either direction. The server is willing to accept content from |
| 729 | the client because the client login has the "write-unversioned" |
| 730 | permission. |
| 731 | |
| 732 | <li><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i> A client sends the "ci-lock" pragma to the server to indicate |
| 733 | that it is about to add a new check-in as a child of the |
| 734 | CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH. |
| 735 | If some other client has already indicated that it was also |
| 736 | trying to commit against CHECKIN-HASH, that indicates that a |
| 737 | fork is about to occur, and the server will reply with |
| 738 | a "ci-lock-fail" pragma (see below). Check-in locks |
| 739 | automatically expire when the check-in actually occurs, or |
| 740 | after a timeout (currently one minute but subject to change). |
| 741 | |
| 742 | <li><b>ci-lock-fail</b> <i>LOGIN MTIME</i> When a server receives two or more "ci-lock" pragma messages |
| 743 | for the same check-in but from different clients, the second a |
| 744 | subsequent ci-lock will provoke a ci-lock-fail pragma in the |
| 745 | reply to let the client know that it if continues with the |
| 746 | check-in it will likely generate a fork. The LOGIN and MTIME |
| 747 | arguments are intended to provide information to the client to |
| 748 | help it generate a more useful error message. |
| 749 | |
| 750 | <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after |
| 751 | a successful commit. This instructs the server to release |
| 752 | any lock on any check-in previously held by that client. |
| 753 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 754 | that might arise if a check-in is aborted and then restarted |
| 755 | on a branch. |
| 756 | </ol> |
| 757 | |
| 758 | <h3>3.12 Comment Cards</h3> |
| 759 | |
| 760 | Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 761 | is silently ignored. |
| 762 | |
| 763 | <h3>3.13 Message and Error Cards</h3> |
| 764 | |
| 765 | If the server discovers anything wrong with a request, it generates |
| 766 | an error card in its reply. When the client sees the error card, |
| 767 | it displays an error message to the user and aborts the sync |
| 768 | operation. An error card looks like this: |
| 769 | |
| 770 | <blockquote> |
| 771 | <b>error</b> <i>error-message</i> |
| 772 | </blockquote> |
| 773 | |
| 774 | The error message is English text that is encoded in order to |
| 775 | be a single token. |
| 776 | A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
| 777 | newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash |
| 778 | (ASCII 0x5C) is represented as two backslashes "\\". Apart from |
| 779 | space and newline, no other whitespace characters nor any |
| 780 | unprintable characters are allowed in |
| 781 | the error message. |
| 782 | |
| 783 | The server can also send a message card that also prints a |
| 784 | message on the client console, but which is not an error: |
| 785 | |
| 786 | <blockquote> |
| 787 | <b>message</b> <i>message-text</i> |
| 788 | </blockquote> |
| 789 | |
| 790 | The message-text uses the same format as an error message. |
| 791 | |
| 792 | <h3>3.14 Unknown Cards</h3> |
| 793 | |
| 794 | If either the client or the server sees a card that is not |
| 795 | described above, then it generates an error and aborts. |
| 796 | |
| 797 | <h2>4.0 Phantoms And Clusters</h2> |
| 798 | |
| 799 | When a repository knows that an artifact exists and knows the ID of |
| 800 | that artifact, but it does not know the artifact content, then it stores that |
| 801 | artifact as a "phantom". A repository will typically create a phantom when |
| 802 | it receives an igot card for an artifact that it does not hold or when it |
| 803 | receives a file card that references a delta source that it does not |
| 804 | hold. When a server is generating its reply or when a client is |
| 805 | generating a new request, it will usually send gimme cards for every |
| 806 | phantom that it holds. |
| 807 | |
| 808 | A cluster is a special artifact that tells of the existence of other |
| 809 | artifacts. Any artifact in the repository that follows the syntactic rules |
| 810 | of a cluster is considered a cluster. |
| 811 | |
| 812 | A cluster is line oriented. Each line of a cluster |
| 813 | is a card. The cards are separated by the newline ("\n") character. |
| 814 | Each card consists of a single character card type, a space, and a |
| 815 | single argument. No extra whitespace and no trailing or leading |
| 816 | whitespace is allowed. All cards in the cluster must occur in |
| 817 | strict lexicographical order. |
| 818 | |
| 819 | A cluster consists of one or more "M" cards followed by a single |
| 820 | "Z" card. Each M card holds an argument which is an artifact ID for an |
| 821 | artifact in the repository. The Z card has a single argument which is the |
| 822 | lower-case hexadecimal representation of the MD5 checksum of all |
| 823 | preceding M cards up to and included the newline character that |
| 824 | occurred just before the Z that starts the Z card. |
| 825 | |
| 826 | Any artifact that does not match the specifications of a cluster |
| 827 | exactly is not a cluster. There must be no extra whitespace in |
| 828 | the artifact. There must be one or more M cards. There must be a |
| 829 | single Z card with a correct MD5 checksum. And all cards must |
| 830 | be in strict lexicographical order. |
| 831 | |
| 832 | <h3>4.1 The Unclustered Table</h3> |
| 833 | |
| 834 | Every repository maintains a table named "<b>unclustered</b>" |
| 835 | which records the identity of every artifact and phantom it holds that is not |
| 836 | mentioned in a cluster. The entries in the unclustered table can |
| 837 | be thought of as leaves on a tree of artifacts. Some of the unclustered |
| 838 | artifacts will be other clusters. Those clusters may contain other clusters, |
| 839 | which might contain still more clusters, and so forth. Beginning |
| 840 | with the artifacts in the unclustered table, one can follow the chain |
| 841 | of clusters to find every artifact in the repository. |
| 842 | |
| 843 | <h2>5.0 Synchronization Strategies</h2> |
| 844 | |
| 845 | <h3>5.1 Pull</h3> |
| 846 | |
| 847 | A typical pull operation proceeds as shown below. Details |
| 848 | of the actual implementation may very slightly but the gist of |
| 849 | a pull is captured in the following steps: |
| 850 | |
| 851 | <ol> |
| 852 | <li>The client sends login and pull cards. |
| 853 | <li>The client sends a cookie card if it has previously received a cookie. |
| 854 | <li>The client sends gimme cards for every phantom that it holds. |
| @@ -877,38 +868,38 @@ | |
| 868 | that mentions an artifact that the client does not possess. |
| 869 | <li>The client creates a phantom for the delta source of file cards when |
| 870 | the delta source is an artifact that the client does not possess. |
| 871 | </ol> |
| 872 | |
| 873 | These ten steps represent a single HTTP round-trip request. |
| 874 | The first three steps are the processing that occurs on the client |
| 875 | to generate the request. The middle four steps are processing |
| 876 | that occurs on the server to interpret the request and generate a |
| 877 | reply. And the last three steps are the processing that the |
| 878 | client does to interpret the reply. |
| 879 | |
| 880 | During a pull, the client will keep sending HTTP requests |
| 881 | until it holds all artifacts that exist on the server. |
| 882 | |
| 883 | Note that the server tries |
| 884 | to limit the size of its reply message to something reasonable |
| 885 | (usually about 1MB) so that it might stop sending file cards as |
| 886 | described in step (6) if the reply becomes too large. |
| 887 | |
| 888 | Step (5) is the only way in which new clusters can be created. |
| 889 | By only creating clusters on the server, we hope to minimize the |
| 890 | amount of overlap between clusters in the common configuration where |
| 891 | there is a single server and many clients. The same synchronization |
| 892 | protocol will continue to work even if there are multiple servers |
| 893 | or if servers and clients sometimes change roles. The only negative |
| 894 | effects of these unusual arrangements is that more than the minimum |
| 895 | number of clusters might be generated. |
| 896 | |
| 897 | <h3>5.2 Push</h3> |
| 898 | |
| 899 | A typical push operation proceeds roughly as shown below. As |
| 900 | with a pull, the actual implementation may vary slightly. |
| 901 | |
| 902 | <ol> |
| 903 | <li>The client sends login and push cards. |
| 904 | <li>The client sends file cards for any artifacts that it holds that have |
| 905 | never before been pushed - artifacts that come from local check-ins. |
| @@ -929,40 +920,40 @@ | |
| 920 | <hr> |
| 921 | <li>The client remembers the gimme cards from the server so that it |
| 922 | can generate file cards in reply on the next cycle. |
| 923 | </ol> |
| 924 | |
| 925 | As with a pull, the steps of a push operation repeat until the |
| 926 | server knows all artifacts that exist on the client. Also, as with |
| 927 | pull, the client attempts to keep the size of the request from |
| 928 | growing too large by suppressing file cards once the |
| 929 | size of the request reaches 1MB. |
| 930 | |
| 931 | <h3 id="sync">5.3 Sync</h3> |
| 932 | |
| 933 | A sync is just a pull and a push that happen at the same time. |
| 934 | The first three steps of a pull are combined with the first five steps |
| 935 | of a push. Steps (4) through (7) of a pull are combined with steps |
| 936 | (5) through (8) of a push. And steps (8) through (10) of a pull |
| 937 | are combined with step (9) of a push. |
| 938 | |
| 939 | <h3>5.4 Unversioned File Sync</h3> |
| 940 | |
| 941 | "Unversioned files" are files held in the repository |
| 942 | where only the most recent version of the file is kept rather than |
| 943 | the entire change history. Unversioned files are intended to be |
| 944 | used to store ephemeral content, such as compiled binaries of the |
| 945 | most recent release. |
| 946 | |
| 947 | Unversioned files are identified by name and timestamp (mtime). |
| 948 | Only the most recent version of each file (the version with |
| 949 | the largest mtime value) is retained. |
| 950 | |
| 951 | Unversioned files are synchronized using the |
| 952 | [/help?cmd=unversioned|fossil unversioned sync] command. |
| 953 | |
| 954 | A schematic of an unversioned file synchronization is as follows: |
| 955 | |
| 956 | <ol> |
| 957 | <li>The client sends a "pragma uv-hash" card to the server. The argument |
| 958 | to the uv-hash pragma is a hash of all filesnames, mtimes, and |
| 959 | content hashes for the unversioned files held by the client. |
| @@ -981,17 +972,17 @@ | |
| 972 | <hr> |
| 973 | <li>The server updates its unversioned file store with received "uvfile" |
| 974 | cards and answers "uvgimme" cards with "uvfile" cards in its reply. |
| 975 | </ol> |
| 976 | |
| 977 | The last two steps might be repeated multiple |
| 978 | times if there is more unversioned content to be transferred than will |
| 979 | fit comfortably in a single HTTP request. |
| 980 | |
| 981 | <h2>6.0 Summary</h2> |
| 982 | |
| 983 | Here are the key points of the synchronization protocol: |
| 984 | |
| 985 | <ol> |
| 986 | <li>The client sends one or more PUSH HTTP requests to the server. |
| 987 | The request and reply content type is "application/x-fossil". |
| 988 | <li>HTTP request content is compressed using zlib. |
| @@ -1031,11 +1022,11 @@ | |
| 1022 | gimme messages for those artifacts. |
| 1023 | </ol> |
| 1024 | |
| 1025 | <h2>7.0 Troubleshooting And Debugging Hints</h2> |
| 1026 | |
| 1027 | |
| 1028 | If you run the [/help?cmd=sync|fossil sync] command |
| 1029 | (or [/help?cmd=pull|pull] or [/help?cmd=push|push] or |
| 1030 | [/help?cmd=clone|clone]) with the --httptrace option, Fossil |
| 1031 | will keep a copy of each HTTP request and reply in files |
| 1032 | named: |
| @@ -1042,17 +1033,17 @@ | |
| 1033 | <ul> |
| 1034 | <li> <tt>http-request-</tt><i>N</i><tt>.txt</tt> |
| 1035 | <li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt> |
| 1036 | </ul> |
| 1037 | |
| 1038 | In the above, <i>N</i> is an integer that increments with each |
| 1039 | round-trip. If you are having trouble on the server side, |
| 1040 | you can run the "[/help?cmd=test-http|fossil test-http]" command in a |
| 1041 | debugger using one the "http-request-N.txt" files as input and |
| 1042 | single step through the processing performed by the server. |
| 1043 | |
| 1044 | The "--transport-command CMD" option on [/help?cmd=sync|fossil sync] |
| 1045 | (and similar) causes the external program "CMD" to be used to move |
| 1046 | the sync message to the server and retrieve the sync reply. The |
| 1047 | CMD is given three arguments: |
| 1048 | <ol> |
| 1049 | <li> The URL of the server |
| @@ -1060,9 +1051,9 @@ | |
| 1051 | protocol text, with the HTTP headers |
| 1052 | <li> The name of a temporary file into which the CMD should write the |
| 1053 | reply sync protocol text, again without any HTTP headers |
| 1054 | </ol> |
| 1055 | |
| 1056 | In a complex debugging situation, you can run the command |
| 1057 | "fossil sync --transport-command ./debugging_script" where |
| 1058 | "debugging_script" is some script of your own that invokes |
| 1059 | the anomolous behavior your are trying to debug. |
| 1060 |
+42
-27
| --- www/unvers.wiki | ||
| +++ www/unvers.wiki | ||
| @@ -1,27 +1,28 @@ | ||
| 1 | 1 | <title>Unversioned Content</title> |
| 2 | 2 | <h1 align="center">Unversioned Content</h1> |
| 3 | 3 | |
| 4 | 4 | "Unversioned content" or "unversioned files" are |
| 5 | -files stored in a Fossil repository without history. | |
| 6 | -Only the newest version of each unversioned file is retained. | |
| 5 | +files stored in a Fossil repository without history, meaning | |
| 6 | +it retains the newest version of each such file, and that alone. | |
| 7 | 7 | |
| 8 | -Though history is omitted, unversioned content is synced between | |
| 9 | -repositories. In the event of a conflict during a sync, the most recent | |
| 10 | -version of each unversioned file is retained and older versions are discarded. | |
| 8 | +Though it omits history, Fossil does sync unversioned content between | |
| 9 | +repositories. In the event of a conflict during a sync, it retains | |
| 10 | +the most recent version of each unversioned file, discrding | |
| 11 | +older versions. | |
| 11 | 12 | |
| 12 | 13 | Unversioned files are useful for storing ephemeral content such as builds |
| 13 | -or frequently changing web pages. | |
| 14 | -The [https://fossil-scm.org/home/uv/download.html|download] page of | |
| 15 | -the self-hosting Fossil repository is stored as unversioned | |
| 14 | +or frequently changing web pages. We store | |
| 15 | +the [https://fossil-scm.org/home/uv/download.html|download] page of | |
| 16 | +the self-hosting Fossil repository as unversioned | |
| 16 | 17 | content, for example. |
| 17 | 18 | |
| 18 | 19 | <h2>Accessing Unversioned Files</h2> |
| 19 | 20 | |
| 20 | 21 | Unversioned files are <u>not</u> a part of a check-out. |
| 21 | 22 | Unversioned files are intended to be accessible as web pages using |
| 22 | -URLs of the form: "http://domain/cgi-script/<b>uv</b>/<i>FILENAME</i>". | |
| 23 | +URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". | |
| 23 | 24 | In other words, the URI method "<b>uv</b>" (short for "unversioned") |
| 24 | 25 | followed by the name of the unversioned file will retrieve the content |
| 25 | 26 | of the file. The MIME type is inferred from the filename suffix. |
| 26 | 27 | |
| 27 | 28 | The content of unversioned files can also be retrieved using the |
| @@ -30,36 +31,43 @@ | ||
| 30 | 31 | A list of all unversioned files on a server can be seen using |
| 31 | 32 | the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]). |
| 32 | 33 | |
| 33 | 34 | <h2>Syncing Unversioned Files</h2> |
| 34 | 35 | |
| 35 | -Unversioned content is synced between repositories, though not by default. | |
| 36 | -Special commands or command-line options are required. | |
| 37 | -Unversioned content can be synced using the following commands: | |
| 36 | +Unversioned content does not sync between repositories by default. | |
| 37 | +One must request it via commands such as: | |
| 38 | 38 | |
| 39 | 39 | <blockquote><pre> |
| 40 | 40 | fossil sync <b>-u</b> |
| 41 | 41 | fossil clone <b>-u</b> <i>URL local-repo-name</i> |
| 42 | 42 | fossil unversioned sync |
| 43 | 43 | </pre></blockquote> |
| 44 | 44 | |
| 45 | 45 | The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] |
| 46 | -commands will synchronize unversioned content if and only if the | |
| 47 | -"-u" (or "--unversioned") command-line option is supplied. The | |
| 48 | -[/help?cmd=unversioned|fossil unversioned sync] command will synchronize the | |
| 46 | +commands will synchronize unversioned content if and only if they're | |
| 47 | +given the "-u" (or "--unversioned") command-line option. The | |
| 48 | +[/help?cmd=unversioned|fossil unversioned sync] command synchronizes the | |
| 49 | 49 | unversioned content without synchronizing anything else. |
| 50 | 50 | |
| 51 | 51 | Notice that the "-u" option does not work on |
| 52 | 52 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 53 | 53 | The "-u" option is only available on "sync" and "clone". |
| 54 | - | |
| 55 | 54 | A rough equivalent of an unversioned pull would be the |
| 56 | 55 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 57 | 56 | "unversioned revert" |
| 58 | 57 | command causes the unversioned content on the local repository to overwritten |
| 59 | 58 | by the unversioned content found on the remote repository. |
| 60 | 59 | |
| 60 | +Beware that because unversioned file sync is an uncommonly dangerous | |
| 61 | +capability — there being no history to revert to in the case of human | |
| 62 | +error — even the all-powerful Fossil "setup" user does not get | |
| 63 | +unversioned file sync capability by default. See | |
| 64 | +[./caps/admin-v-setup.md#dcap | this] for the full details, but the | |
| 65 | +short-and-sweet takeaway is that a user needs the "y" capability on the | |
| 66 | +remote before they can sync unversioned content. Until then, you're | |
| 67 | +allowed to add such files to your local repo, but they will not sync. | |
| 68 | + | |
| 61 | 69 | <h2>Implementation Details</h2> |
| 62 | 70 | |
| 63 | 71 | <i>(This section outlines the current implementation of unversioned |
| 64 | 72 | files. This is not an interface spec and hence subject to change.)</i> |
| 65 | 73 | |
| @@ -77,23 +85,30 @@ | ||
| 77 | 85 | encoding INT, -- 0: plaintext 1: zlib compressed |
| 78 | 86 | content BLOB -- File content |
| 79 | 87 | ); |
| 80 | 88 | </pre></blockquote> |
| 81 | 89 | |
| 82 | -If there are no unversioned files in the repository, then the | |
| 83 | -"unversioned" table does not necessarily exist. | |
| 84 | -A simple way to purge all unversioned content from a repository | |
| 90 | +Fossil does not create the table ahead of need. | |
| 91 | +If there are no unversioned files in the repository, the | |
| 92 | +"unversioned" table will not exist. Consequently, | |
| 93 | +one simple way to purge all unversioned content from a repository | |
| 85 | 94 | is to run: |
| 86 | 95 | |
| 87 | 96 | <blockquote><pre> |
| 88 | 97 | fossil sql "DROP TABLE unversioned; VACUUM;" |
| 89 | 98 | </pre></blockquote> |
| 90 | 99 | |
| 91 | -No delta compression is performed on unversioned files, since there is no | |
| 92 | -history to delta against. | |
| 93 | - | |
| 94 | -Unversioned content is exchanged between servers as whole, uncompressed | |
| 95 | -files (though the content does get compressed when the overall HTTP message | |
| 96 | -payload is compressed). SHA1 hash exchanges are used to avoid sending | |
| 97 | -content over the wire unnecessarily. See the | |
| 98 | -[./sync.wiki|synchronization protocol documentation] for further | |
| 100 | +Lacking history for unversioned files, Fossil does not attempt delta | |
| 101 | +compression on them. | |
| 102 | + | |
| 103 | +Fossil servers exchange unversioned content whole; it does not attempt | |
| 104 | +to "diff" your local version against the remote and send only the | |
| 105 | +changes. We point tihs out because one use-case for unversioned content | |
| 106 | +is to send large, frequently-changing files. Appreciate the consequences | |
| 107 | +before making each change. | |
| 108 | + | |
| 109 | +There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". | |
| 110 | +The first is the regular HTTP payload compression step, done on all | |
| 111 | +syncs. The second is that Fossil sends SHA1 hash exchanges to determine | |
| 112 | +when it can avoid sending duplicate content over the wire unnecessarily. | |
| 113 | +See the [./sync.wiki|synchronization protocol documentation] for further | |
| 99 | 114 | information. |
| 100 | 115 |
| --- www/unvers.wiki | |
| +++ www/unvers.wiki | |
| @@ -1,27 +1,28 @@ | |
| 1 | <title>Unversioned Content</title> |
| 2 | <h1 align="center">Unversioned Content</h1> |
| 3 | |
| 4 | "Unversioned content" or "unversioned files" are |
| 5 | files stored in a Fossil repository without history. |
| 6 | Only the newest version of each unversioned file is retained. |
| 7 | |
| 8 | Though history is omitted, unversioned content is synced between |
| 9 | repositories. In the event of a conflict during a sync, the most recent |
| 10 | version of each unversioned file is retained and older versions are discarded. |
| 11 | |
| 12 | Unversioned files are useful for storing ephemeral content such as builds |
| 13 | or frequently changing web pages. |
| 14 | The [https://fossil-scm.org/home/uv/download.html|download] page of |
| 15 | the self-hosting Fossil repository is stored as unversioned |
| 16 | content, for example. |
| 17 | |
| 18 | <h2>Accessing Unversioned Files</h2> |
| 19 | |
| 20 | Unversioned files are <u>not</u> a part of a check-out. |
| 21 | Unversioned files are intended to be accessible as web pages using |
| 22 | URLs of the form: "http://domain/cgi-script/<b>uv</b>/<i>FILENAME</i>". |
| 23 | In other words, the URI method "<b>uv</b>" (short for "unversioned") |
| 24 | followed by the name of the unversioned file will retrieve the content |
| 25 | of the file. The MIME type is inferred from the filename suffix. |
| 26 | |
| 27 | The content of unversioned files can also be retrieved using the |
| @@ -30,36 +31,43 @@ | |
| 30 | A list of all unversioned files on a server can be seen using |
| 31 | the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]). |
| 32 | |
| 33 | <h2>Syncing Unversioned Files</h2> |
| 34 | |
| 35 | Unversioned content is synced between repositories, though not by default. |
| 36 | Special commands or command-line options are required. |
| 37 | Unversioned content can be synced using the following commands: |
| 38 | |
| 39 | <blockquote><pre> |
| 40 | fossil sync <b>-u</b> |
| 41 | fossil clone <b>-u</b> <i>URL local-repo-name</i> |
| 42 | fossil unversioned sync |
| 43 | </pre></blockquote> |
| 44 | |
| 45 | The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] |
| 46 | commands will synchronize unversioned content if and only if the |
| 47 | "-u" (or "--unversioned") command-line option is supplied. The |
| 48 | [/help?cmd=unversioned|fossil unversioned sync] command will synchronize the |
| 49 | unversioned content without synchronizing anything else. |
| 50 | |
| 51 | Notice that the "-u" option does not work on |
| 52 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 53 | The "-u" option is only available on "sync" and "clone". |
| 54 | |
| 55 | A rough equivalent of an unversioned pull would be the |
| 56 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 57 | "unversioned revert" |
| 58 | command causes the unversioned content on the local repository to overwritten |
| 59 | by the unversioned content found on the remote repository. |
| 60 | |
| 61 | <h2>Implementation Details</h2> |
| 62 | |
| 63 | <i>(This section outlines the current implementation of unversioned |
| 64 | files. This is not an interface spec and hence subject to change.)</i> |
| 65 | |
| @@ -77,23 +85,30 @@ | |
| 77 | encoding INT, -- 0: plaintext 1: zlib compressed |
| 78 | content BLOB -- File content |
| 79 | ); |
| 80 | </pre></blockquote> |
| 81 | |
| 82 | If there are no unversioned files in the repository, then the |
| 83 | "unversioned" table does not necessarily exist. |
| 84 | A simple way to purge all unversioned content from a repository |
| 85 | is to run: |
| 86 | |
| 87 | <blockquote><pre> |
| 88 | fossil sql "DROP TABLE unversioned; VACUUM;" |
| 89 | </pre></blockquote> |
| 90 | |
| 91 | No delta compression is performed on unversioned files, since there is no |
| 92 | history to delta against. |
| 93 | |
| 94 | Unversioned content is exchanged between servers as whole, uncompressed |
| 95 | files (though the content does get compressed when the overall HTTP message |
| 96 | payload is compressed). SHA1 hash exchanges are used to avoid sending |
| 97 | content over the wire unnecessarily. See the |
| 98 | [./sync.wiki|synchronization protocol documentation] for further |
| 99 | information. |
| 100 |
| --- www/unvers.wiki | |
| +++ www/unvers.wiki | |
| @@ -1,27 +1,28 @@ | |
| 1 | <title>Unversioned Content</title> |
| 2 | <h1 align="center">Unversioned Content</h1> |
| 3 | |
| 4 | "Unversioned content" or "unversioned files" are |
| 5 | files stored in a Fossil repository without history, meaning |
| 6 | it retains the newest version of each such file, and that alone. |
| 7 | |
| 8 | Though it omits history, Fossil does sync unversioned content between |
| 9 | repositories. In the event of a conflict during a sync, it retains |
| 10 | the most recent version of each unversioned file, discrding |
| 11 | older versions. |
| 12 | |
| 13 | Unversioned files are useful for storing ephemeral content such as builds |
| 14 | or frequently changing web pages. We store |
| 15 | the [https://fossil-scm.org/home/uv/download.html|download] page of |
| 16 | the self-hosting Fossil repository as unversioned |
| 17 | content, for example. |
| 18 | |
| 19 | <h2>Accessing Unversioned Files</h2> |
| 20 | |
| 21 | Unversioned files are <u>not</u> a part of a check-out. |
| 22 | Unversioned files are intended to be accessible as web pages using |
| 23 | URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". |
| 24 | In other words, the URI method "<b>uv</b>" (short for "unversioned") |
| 25 | followed by the name of the unversioned file will retrieve the content |
| 26 | of the file. The MIME type is inferred from the filename suffix. |
| 27 | |
| 28 | The content of unversioned files can also be retrieved using the |
| @@ -30,36 +31,43 @@ | |
| 31 | A list of all unversioned files on a server can be seen using |
| 32 | the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]). |
| 33 | |
| 34 | <h2>Syncing Unversioned Files</h2> |
| 35 | |
| 36 | Unversioned content does not sync between repositories by default. |
| 37 | One must request it via commands such as: |
| 38 | |
| 39 | <blockquote><pre> |
| 40 | fossil sync <b>-u</b> |
| 41 | fossil clone <b>-u</b> <i>URL local-repo-name</i> |
| 42 | fossil unversioned sync |
| 43 | </pre></blockquote> |
| 44 | |
| 45 | The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] |
| 46 | commands will synchronize unversioned content if and only if they're |
| 47 | given the "-u" (or "--unversioned") command-line option. The |
| 48 | [/help?cmd=unversioned|fossil unversioned sync] command synchronizes the |
| 49 | unversioned content without synchronizing anything else. |
| 50 | |
| 51 | Notice that the "-u" option does not work on |
| 52 | [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. |
| 53 | The "-u" option is only available on "sync" and "clone". |
| 54 | A rough equivalent of an unversioned pull would be the |
| 55 | [/help?cmd=unversioned|fossil unversioned revert] command. The |
| 56 | "unversioned revert" |
| 57 | command causes the unversioned content on the local repository to overwritten |
| 58 | by the unversioned content found on the remote repository. |
| 59 | |
| 60 | Beware that because unversioned file sync is an uncommonly dangerous |
| 61 | capability — there being no history to revert to in the case of human |
| 62 | error — even the all-powerful Fossil "setup" user does not get |
| 63 | unversioned file sync capability by default. See |
| 64 | [./caps/admin-v-setup.md#dcap | this] for the full details, but the |
| 65 | short-and-sweet takeaway is that a user needs the "y" capability on the |
| 66 | remote before they can sync unversioned content. Until then, you're |
| 67 | allowed to add such files to your local repo, but they will not sync. |
| 68 | |
| 69 | <h2>Implementation Details</h2> |
| 70 | |
| 71 | <i>(This section outlines the current implementation of unversioned |
| 72 | files. This is not an interface spec and hence subject to change.)</i> |
| 73 | |
| @@ -77,23 +85,30 @@ | |
| 85 | encoding INT, -- 0: plaintext 1: zlib compressed |
| 86 | content BLOB -- File content |
| 87 | ); |
| 88 | </pre></blockquote> |
| 89 | |
| 90 | Fossil does not create the table ahead of need. |
| 91 | If there are no unversioned files in the repository, the |
| 92 | "unversioned" table will not exist. Consequently, |
| 93 | one simple way to purge all unversioned content from a repository |
| 94 | is to run: |
| 95 | |
| 96 | <blockquote><pre> |
| 97 | fossil sql "DROP TABLE unversioned; VACUUM;" |
| 98 | </pre></blockquote> |
| 99 | |
| 100 | Lacking history for unversioned files, Fossil does not attempt delta |
| 101 | compression on them. |
| 102 | |
| 103 | Fossil servers exchange unversioned content whole; it does not attempt |
| 104 | to "diff" your local version against the remote and send only the |
| 105 | changes. We point tihs out because one use-case for unversioned content |
| 106 | is to send large, frequently-changing files. Appreciate the consequences |
| 107 | before making each change. |
| 108 | |
| 109 | There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". |
| 110 | The first is the regular HTTP payload compression step, done on all |
| 111 | syncs. The second is that Fossil sends SHA1 hash exchanges to determine |
| 112 | when it can avoid sending duplicate content over the wire unnecessarily. |
| 113 | See the [./sync.wiki|synchronization protocol documentation] for further |
| 114 | information. |
| 115 |
+10
-5
| --- www/wikitheory.wiki | ||
| +++ www/wikitheory.wiki | ||
| @@ -4,11 +4,12 @@ | ||
| 4 | 4 | Fossil uses [/wiki_rules | Fossil wiki markup] and/or |
| 5 | 5 | [/md_rules | Markdown markup] for many things: |
| 6 | 6 | |
| 7 | 7 | * Stand-alone wiki pages. |
| 8 | 8 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 9 | - * Check-in comments. | |
| 9 | + * Check-in comments. (For historical reasons, these must | |
| 10 | + currently be in fossil-wiki text format.) | |
| 10 | 11 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 11 | 12 | name ends in ".wiki" or ".md" or ".markdown". |
| 12 | 13 | * [./event.wiki | Technical notes]. |
| 13 | 14 | * [./forum.wiki | Forum messages]. |
| 14 | 15 | * Auxiliary notes on check-ins and branches. |
| @@ -60,14 +61,18 @@ | ||
| 60 | 61 | format is used, it is trivial to move a file from wiki to embedded documentation |
| 61 | 62 | or back again as the project evolves. |
| 62 | 63 | |
| 63 | 64 | <h2>Bug-reports and check-in comments and Forum messages</h2> |
| 64 | 65 | |
| 65 | -The comments on check-ins and the text in the descriptions of bug reports | |
| 66 | -both use wiki formatting. Exactly the same set of formatting rules apply. | |
| 67 | -There is never a need to learn one formatting language for documentation | |
| 68 | -and a different markup for bugs or for check-in comments. | |
| 66 | +The comments on check-ins, forum posts, and the text in the | |
| 67 | +descriptions of bug reports both use wiki formatting. Exactly the | |
| 68 | +same set of formatting rules apply. There is never a need to learn | |
| 69 | +one formatting language for documentation and a different markup for | |
| 70 | +bugs or for check-in comments. | |
| 71 | + | |
| 72 | +Minor caveat: check-in messages are currently limited to the | |
| 73 | +fossil-wiki format. | |
| 69 | 74 | |
| 70 | 75 | <h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2> |
| 71 | 76 | |
| 72 | 77 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 73 | 78 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 74 | 79 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -4,11 +4,12 @@ | |
| 4 | Fossil uses [/wiki_rules | Fossil wiki markup] and/or |
| 5 | [/md_rules | Markdown markup] for many things: |
| 6 | |
| 7 | * Stand-alone wiki pages. |
| 8 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 9 | * Check-in comments. |
| 10 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 11 | name ends in ".wiki" or ".md" or ".markdown". |
| 12 | * [./event.wiki | Technical notes]. |
| 13 | * [./forum.wiki | Forum messages]. |
| 14 | * Auxiliary notes on check-ins and branches. |
| @@ -60,14 +61,18 @@ | |
| 60 | format is used, it is trivial to move a file from wiki to embedded documentation |
| 61 | or back again as the project evolves. |
| 62 | |
| 63 | <h2>Bug-reports and check-in comments and Forum messages</h2> |
| 64 | |
| 65 | The comments on check-ins and the text in the descriptions of bug reports |
| 66 | both use wiki formatting. Exactly the same set of formatting rules apply. |
| 67 | There is never a need to learn one formatting language for documentation |
| 68 | and a different markup for bugs or for check-in comments. |
| 69 | |
| 70 | <h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2> |
| 71 | |
| 72 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 73 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 74 |
| --- www/wikitheory.wiki | |
| +++ www/wikitheory.wiki | |
| @@ -4,11 +4,12 @@ | |
| 4 | Fossil uses [/wiki_rules | Fossil wiki markup] and/or |
| 5 | [/md_rules | Markdown markup] for many things: |
| 6 | |
| 7 | * Stand-alone wiki pages. |
| 8 | * Description and comments in [./bugtheory.wiki | bug reports]. |
| 9 | * Check-in comments. (For historical reasons, these must |
| 10 | currently be in fossil-wiki text format.) |
| 11 | * [./embeddeddoc.wiki | Embedded documentation] files whose |
| 12 | name ends in ".wiki" or ".md" or ".markdown". |
| 13 | * [./event.wiki | Technical notes]. |
| 14 | * [./forum.wiki | Forum messages]. |
| 15 | * Auxiliary notes on check-ins and branches. |
| @@ -60,14 +61,18 @@ | |
| 61 | format is used, it is trivial to move a file from wiki to embedded documentation |
| 62 | or back again as the project evolves. |
| 63 | |
| 64 | <h2>Bug-reports and check-in comments and Forum messages</h2> |
| 65 | |
| 66 | The comments on check-ins, forum posts, and the text in the |
| 67 | descriptions of bug reports both use wiki formatting. Exactly the |
| 68 | same set of formatting rules apply. There is never a need to learn |
| 69 | one formatting language for documentation and a different markup for |
| 70 | bugs or for check-in comments. |
| 71 | |
| 72 | Minor caveat: check-in messages are currently limited to the |
| 73 | fossil-wiki format. |
| 74 | |
| 75 | <h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2> |
| 76 | |
| 77 | Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>" |
| 78 | or "checkin/<i>HASH</i>" are associated with the corresponding |
| 79 |