Fossil SCM

Merge trunk into forumpost-locking branch.

stephan 2023-06-03 08:49 UTC forumpost-locking merge
Commit 8e7de26aa2680f225080590781f5e2d6d82f99cd27cfdb8fd51aa5539a97ac9e
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 @@
88
### We aren't pinning to a more stable version of Alpine because we want
99
### to build with the latest tools and libraries available in case they
1010
### fixed something that matters to us since the last build. Everything
1111
### below depends on this layer, and so, alas, we toss this container's
1212
### 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
1515
1616
### Bake the basic Alpine Linux into a base layer so it only changes
1717
### when the upstream image is updated or we change the package set.
1818
RUN set -x \
1919
&& apk update \
@@ -35,17 +35,17 @@
3535
### by leveraging its DVCS nature via the "tarball" command and passing
3636
### the resulting file's name in.
3737
ARG FSLCFG=""
3838
ARG FSLVER="trunk"
3939
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
4141
ADD $FSLURL $FSLSTB
4242
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
4747
4848
4949
## ---------------------------------------------------------------------
5050
## STAGE 2: Pare that back to the bare essentials.
5151
## ---------------------------------------------------------------------
@@ -54,28 +54,28 @@
5454
ARG UID=499
5555
5656
### Set up that base OS for our specific use without tying it to
5757
### anything likely to change often. So long as the user leaves
5858
### 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
6565
6666
6767
## ---------------------------------------------------------------------
6868
## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c
6969
## ---------------------------------------------------------------------
7070
7171
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/
7777
7878
7979
## ---------------------------------------------------------------------
8080
## RUN!
8181
## ---------------------------------------------------------------------
@@ -82,9 +82,9 @@
8282
8383
ENV PATH "/bin"
8484
EXPOSE 8080/tcp
8585
USER fossil
8686
ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ]
87
-CMD [ \
87
+CMD [ \
8888
"--create", \
8989
"--jsmode", "bundled", \
90
- "--user", "admin" ]
90
+ "--user", "admin" ]
9191
--- 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
+1 -1
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1
-2.22
1
+2.23
22
--- 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 @@
5959
** cc pikchr.c -o pikchr.o
6060
**
6161
** Add -DPIKCHR_SHELL to add a main() routine that reads input files
6262
** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for
6363
** -fsanitizer=fuzzer testing.
64
-**
64
+**
6565
****************************************************************************
6666
** IMPLEMENTATION NOTES (for people who want to understand the internal
6767
** operation of this software, perhaps to extend the code or to fix bugs):
6868
**
6969
** Each call to pikchr() uses a single instance of the Pik structure to
@@ -203,12 +203,13 @@
203203
#define TP_SMALL 0x0200 /* Smaller font */
204204
#define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */
205205
#define TP_SZMASK 0x0700 /* Font size mask */
206206
#define TP_ITALIC 0x1000 /* Italic font */
207207
#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 */
210211
211212
/* An object to hold a position in 2-D space */
212213
struct PPoint {
213214
PNum x, y; /* X and Y coordinates */
214215
};
@@ -470,11 +471,11 @@
470471
static void pik_bbox_init(PBox*);
471472
static void pik_bbox_addbox(PBox*,PBox*);
472473
static void pik_bbox_add_xy(PBox*,PNum,PNum);
473474
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
474475
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);
476477
static void pik_size_to_fit(Pik*,PToken*,int);
477478
static int pik_text_position(int,PToken*);
478479
static PNum pik_property_of(PObj*,PToken*);
479480
static PNum pik_func(Pik*,PToken*,PNum,PNum);
480481
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
@@ -489,11 +490,11 @@
489490
static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
490491
static PNum pik_dist(PPoint*,PPoint*);
491492
static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);
492493
493494
494
-#line 520 "pikchr.c"
495
+#line 521 "pikchr.c"
495496
/**************** End of %include directives **********************************/
496497
/* These constants specify the various numeric values for terminal symbols.
497498
***************** Begin token definitions *************************************/
498499
#ifndef T_ID
499500
#define T_ID 1
@@ -564,38 +565,39 @@
564565
#define T_RJUST 66
565566
#define T_ABOVE 67
566567
#define T_BELOW 68
567568
#define T_ITALIC 69
568569
#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
597599
#endif
598600
/**************** End token definitions ***************************************/
599601
600602
/* The next sections is a series of control #defines.
601603
** various aspects of the generated parser.
@@ -651,22 +653,22 @@
651653
#ifndef INTERFACE
652654
# define INTERFACE 1
653655
#endif
654656
/************* Begin control #defines *****************************************/
655657
#define YYCODETYPE unsigned char
656
-#define YYNOCODE 135
658
+#define YYNOCODE 136
657659
#define YYACTIONTYPE unsigned short int
658660
#define pik_parserTOKENTYPE PToken
659661
typedef union {
660662
int yyinit;
661663
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;
668670
} YYMINORTYPE;
669671
#ifndef YYSTACKDEPTH
670672
#define YYSTACKDEPTH 100
671673
#endif
672674
#define pik_parserARG_SDECL
@@ -681,11 +683,11 @@
681683
#define pik_parserCTX_STORE yypParser->p=p;
682684
#define YYFALLBACK 1
683685
#define YYNSTATE 164
684686
#define YYNRULE 156
685687
#define YYNRULE_WITH_ACTION 116
686
-#define YYNTOKEN 99
688
+#define YYNTOKEN 100
687689
#define YY_MAX_SHIFT 163
688690
#define YY_MIN_SHIFTREDUCE 287
689691
#define YY_MAX_SHIFTREDUCE 442
690692
#define YY_ERROR_ACTION 443
691693
#define YY_ACCEPT_ACTION 444
@@ -756,322 +758,324 @@
756758
** yy_reduce_ofst[] For each state, the offset into yy_action for
757759
** shifting non-terminals after a reduce.
758760
** yy_default[] Default action for each state.
759761
**
760762
*********** Begin parsing tables **********************************************/
761
-#define YY_ACTTAB_COUNT (1303)
763
+#define YY_ACTTAB_COUNT (1313)
762764
static const YYACTIONTYPE yy_action[] = {
763765
/* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148,
764766
/* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530,
765767
/* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64,
766768
/* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32,
767769
/* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339,
768770
/* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344,
769771
/* 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,
773775
/* 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,
775777
/* 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,
894897
};
895898
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,
901904
/* 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,
908911
/* 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,
10371041
};
10381042
#define YY_SHIFT_COUNT (163)
10391043
#define YY_SHIFT_MIN (0)
1040
-#define YY_SHIFT_MAX (1277)
1044
+#define YY_SHIFT_MAX (1113)
10411045
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,
10591063
};
10601064
#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)
10631067
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,
10731077
};
10741078
static const YYACTIONTYPE yy_default[] = {
10751079
/* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443,
10761080
/* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
10771081
/* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576,
@@ -1177,10 +1181,11 @@
11771181
0, /* RJUST => nothing */
11781182
0, /* ABOVE => nothing */
11791183
0, /* BELOW => nothing */
11801184
0, /* ITALIC => nothing */
11811185
0, /* BOLD => nothing */
1186
+ 0, /* MONO => nothing */
11821187
0, /* ALIGNED => nothing */
11831188
0, /* BIG => nothing */
11841189
0, /* SMALL => nothing */
11851190
0, /* AND => nothing */
11861191
0, /* LT => nothing */
@@ -1364,74 +1369,75 @@
13641369
/* 66 */ "RJUST",
13651370
/* 67 */ "ABOVE",
13661371
/* 68 */ "BELOW",
13671372
/* 69 */ "ITALIC",
13681373
/* 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",
14331439
};
14341440
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
14351441
14361442
#ifndef NDEBUG
14371443
/* For tracing reduce actions, the names of all rules are required.
@@ -1498,11 +1504,11 @@
14981504
/* 58 */ "boolproperty ::= INVIS",
14991505
/* 59 */ "boolproperty ::= THICK",
15001506
/* 60 */ "boolproperty ::= THIN",
15011507
/* 61 */ "boolproperty ::= SOLID",
15021508
/* 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",
15041510
/* 64 */ "position ::= expr COMMA expr",
15051511
/* 65 */ "position ::= place PLUS expr COMMA expr",
15061512
/* 66 */ "position ::= place MINUS expr COMMA expr",
15071513
/* 67 */ "position ::= place PLUS LP expr COMMA expr RP",
15081514
/* 68 */ "position ::= place MINUS LP expr COMMA expr RP",
@@ -1716,24 +1722,24 @@
17161722
** Note: during a reduce, the only symbols destroyed are those
17171723
** which appear on the RHS of the rule, but which are *not* used
17181724
** inside the C code.
17191725
*/
17201726
/********* 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"
17351741
}
17361742
break;
17371743
/********* End destructor definitions *****************************************/
17381744
default: break; /* If no destructor action specified: do nothing */
17391745
}
@@ -1947,14 +1953,14 @@
19471953
#endif
19481954
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
19491955
/* Here code is inserted which will execute if the parser
19501956
** stack every overflows */
19511957
/******** Begin %stack_overflow code ******************************************/
1952
-#line 543 "pikchr.y"
1958
+#line 544 "pikchr.y"
19531959
19541960
pik_error(p, 0, "parser stack overflow");
1955
-#line 1980 "pikchr.c"
1961
+#line 1986 "pikchr.c"
19561962
/******** End %stack_overflow code ********************************************/
19571963
pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
19581964
pik_parserCTX_STORE
19591965
}
19601966
@@ -2022,166 +2028,166 @@
20222028
}
20232029
20242030
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
20252031
** of that rule */
20262032
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 */
21832189
};
21842190
21852191
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
21862192
** of symbols on the right-hand side of that rule. */
21872193
static const signed char yyRuleInfoNRhs[] = {
@@ -2246,11 +2252,11 @@
22462252
-1, /* (58) boolproperty ::= INVIS */
22472253
-1, /* (59) boolproperty ::= THICK */
22482254
-1, /* (60) boolproperty ::= THIN */
22492255
-1, /* (61) boolproperty ::= SOLID */
22502256
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 */
22522258
-3, /* (64) position ::= expr COMMA expr */
22532259
-5, /* (65) position ::= place PLUS expr COMMA expr */
22542260
-5, /* (66) position ::= place MINUS expr COMMA expr */
22552261
-7, /* (67) position ::= place PLUS LP expr COMMA expr RP */
22562262
-7, /* (68) position ::= place MINUS LP expr COMMA expr RP */
@@ -2381,620 +2387,620 @@
23812387
** break;
23822388
*/
23832389
/********** Begin reduce actions **********************************************/
23842390
YYMINORTYPE yylhsminor;
23852391
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"
23892395
break;
23902396
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;
23952401
break;
23962402
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;
24012407
break;
24022408
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"
24062412
break;
24072413
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;
24122418
break;
24132419
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;
24182424
break;
24192425
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;
24242430
break;
24252431
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;
24312437
break;
24322438
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;
24372443
break;
24382444
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"
24422448
break;
24432449
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"
24472453
break;
24482454
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"
24522458
break;
24532459
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"
24572463
break;
24582464
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;
24632469
break;
24642470
case 14: /* pritem ::= FILL */
24652471
case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
24662472
case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
2467
-#line 588 "pikchr.y"
2473
+#line 589 "pikchr.y"
24682474
{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"
24702476
break;
24712477
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"
24752481
break;
24762482
case 18: /* pritem ::= STRING */
2477
-#line 592 "pikchr.y"
2483
+#line 593 "pikchr.y"
24782484
{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"
24802486
break;
24812487
case 19: /* prsep ::= COMMA */
2482
-#line 593 "pikchr.y"
2488
+#line 594 "pikchr.y"
24832489
{pik_append(p, " ", 1);}
2484
-#line 2509 "pikchr.c"
2490
+#line 2515 "pikchr.c"
24852491
break;
24862492
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;
24912497
break;
24922498
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;
24972503
break;
24982504
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;
25032509
break;
25042510
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"
25082514
break;
25092515
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"
25132519
break;
25142520
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;
25192525
break;
25202526
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;
25252531
break;
25262532
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"
25302536
break;
25312537
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"
25352541
break;
25362542
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"
25402546
break;
25412547
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"
25452551
break;
25462552
case 31: /* attribute ::= dashproperty */
2547
-#line 625 "pikchr.y"
2553
+#line 626 "pikchr.y"
25482554
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
2549
-#line 2574 "pikchr.c"
2555
+#line 2580 "pikchr.c"
25502556
break;
25512557
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"
25552561
break;
25562562
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"
25602566
break;
25612567
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"
25652571
break;
25662572
case 35: /* attribute ::= CLOSE */
2567
-#line 629 "pikchr.y"
2573
+#line 630 "pikchr.y"
25682574
{ pik_close_path(p,&yymsp[0].minor.yy0); }
2569
-#line 2594 "pikchr.c"
2575
+#line 2600 "pikchr.c"
25702576
break;
25712577
case 36: /* attribute ::= CHOP */
2572
-#line 630 "pikchr.y"
2578
+#line 631 "pikchr.y"
25732579
{ p->cur->bChop = 1; }
2574
-#line 2599 "pikchr.c"
2580
+#line 2605 "pikchr.c"
25752581
break;
25762582
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"
25802586
break;
25812587
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"
25852591
break;
25862592
case 39: /* attribute ::= THEN */
2587
-#line 633 "pikchr.y"
2593
+#line 634 "pikchr.y"
25882594
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
2589
-#line 2614 "pikchr.c"
2595
+#line 2620 "pikchr.c"
25902596
break;
25912597
case 40: /* attribute ::= THEN optrelexpr HEADING expr */
25922598
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"
25962602
break;
25972603
case 41: /* attribute ::= THEN optrelexpr EDGEPT */
25982604
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"
26022608
break;
26032609
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"
26072613
break;
26082614
case 45: /* attribute ::= SAME */
2609
-#line 643 "pikchr.y"
2615
+#line 644 "pikchr.y"
26102616
{pik_same(p,0,&yymsp[0].minor.yy0);}
2611
-#line 2636 "pikchr.c"
2617
+#line 2642 "pikchr.c"
26122618
break;
26132619
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"
26172623
break;
26182624
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"
26222628
break;
26232629
case 48: /* attribute ::= FIT */
2624
-#line 646 "pikchr.y"
2630
+#line 647 "pikchr.y"
26252631
{pik_size_to_fit(p,&yymsp[0].minor.yy0,3); }
2626
-#line 2651 "pikchr.c"
2632
+#line 2657 "pikchr.c"
26272633
break;
26282634
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"
26322638
break;
26332639
case 50: /* withclause ::= DOT_E edge AT position */
26342640
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"
26382644
break;
26392645
case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
2640
-#line 659 "pikchr.y"
2646
+#line 660 "pikchr.y"
26412647
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
2642
-#line 2667 "pikchr.c"
2648
+#line 2673 "pikchr.c"
26432649
yymsp[0].minor.yy0 = yylhsminor.yy0;
26442650
break;
26452651
case 53: /* boolproperty ::= CW */
2646
-#line 670 "pikchr.y"
2652
+#line 671 "pikchr.y"
26472653
{p->cur->cw = 1;}
2648
-#line 2673 "pikchr.c"
2654
+#line 2679 "pikchr.c"
26492655
break;
26502656
case 54: /* boolproperty ::= CCW */
2651
-#line 671 "pikchr.y"
2657
+#line 672 "pikchr.y"
26522658
{p->cur->cw = 0;}
2653
-#line 2678 "pikchr.c"
2659
+#line 2684 "pikchr.c"
26542660
break;
26552661
case 55: /* boolproperty ::= LARROW */
2656
-#line 672 "pikchr.y"
2662
+#line 673 "pikchr.y"
26572663
{p->cur->larrow=1; p->cur->rarrow=0; }
2658
-#line 2683 "pikchr.c"
2664
+#line 2689 "pikchr.c"
26592665
break;
26602666
case 56: /* boolproperty ::= RARROW */
2661
-#line 673 "pikchr.y"
2667
+#line 674 "pikchr.y"
26622668
{p->cur->larrow=0; p->cur->rarrow=1; }
2663
-#line 2688 "pikchr.c"
2669
+#line 2694 "pikchr.c"
26642670
break;
26652671
case 57: /* boolproperty ::= LRARROW */
2666
-#line 674 "pikchr.y"
2672
+#line 675 "pikchr.y"
26672673
{p->cur->larrow=1; p->cur->rarrow=1; }
2668
-#line 2693 "pikchr.c"
2674
+#line 2699 "pikchr.c"
26692675
break;
26702676
case 58: /* boolproperty ::= INVIS */
2671
-#line 675 "pikchr.y"
2677
+#line 676 "pikchr.y"
26722678
{p->cur->sw = 0.0;}
2673
-#line 2698 "pikchr.c"
2679
+#line 2704 "pikchr.c"
26742680
break;
26752681
case 59: /* boolproperty ::= THICK */
2676
-#line 676 "pikchr.y"
2682
+#line 677 "pikchr.y"
26772683
{p->cur->sw *= 1.5;}
2678
-#line 2703 "pikchr.c"
2684
+#line 2709 "pikchr.c"
26792685
break;
26802686
case 60: /* boolproperty ::= THIN */
2681
-#line 677 "pikchr.y"
2687
+#line 678 "pikchr.y"
26822688
{p->cur->sw *= 0.67;}
2683
-#line 2708 "pikchr.c"
2689
+#line 2714 "pikchr.c"
26842690
break;
26852691
case 61: /* boolproperty ::= SOLID */
2686
-#line 678 "pikchr.y"
2692
+#line 679 "pikchr.y"
26872693
{p->cur->sw = pik_value(p,"thickness",9,0);
26882694
p->cur->dotted = p->cur->dashed = 0.0;}
2689
-#line 2714 "pikchr.c"
2695
+#line 2720 "pikchr.c"
26902696
break;
26912697
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"
26952701
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;
27012707
break;
27022708
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;
27072713
break;
27082714
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;
27132719
break;
27142720
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;
27192725
break;
27202726
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;
27252731
break;
27262732
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;
27312737
break;
27322738
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"
27362742
break;
27372743
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"
27412747
break;
27422748
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;
27472753
break;
27482754
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;
27532759
break;
27542760
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;
27592765
break;
27602766
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;
27652771
break;
27662772
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;
27712777
break;
27722778
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;
27772783
break;
27782784
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;
27832789
break;
27842790
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;
27892795
break;
27902796
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;
27952801
break;
27962802
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;
28012807
break;
28022808
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;
28072813
break;
28082814
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;
28132819
break;
28142820
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;
28192825
break;
28202826
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;
28252831
break;
28262832
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;
28312837
break;
28322838
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;
28372843
break;
28382844
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;
28432849
break;
28442850
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"
28482854
break;
28492855
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;
28542860
break;
28552861
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;
28602866
break;
28612867
case 91: /* nth ::= NTH CLASSNAME */
2862
-#line 749 "pikchr.y"
2868
+#line 750 "pikchr.y"
28632869
{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"
28652871
yymsp[-1].minor.yy0 = yylhsminor.yy0;
28662872
break;
28672873
case 92: /* nth ::= NTH LAST CLASSNAME */
2868
-#line 750 "pikchr.y"
2874
+#line 751 "pikchr.y"
28692875
{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"
28712877
yymsp[-2].minor.yy0 = yylhsminor.yy0;
28722878
break;
28732879
case 93: /* nth ::= LAST CLASSNAME */
2874
-#line 751 "pikchr.y"
2880
+#line 752 "pikchr.y"
28752881
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
2876
-#line 2901 "pikchr.c"
2882
+#line 2907 "pikchr.c"
28772883
break;
28782884
case 94: /* nth ::= LAST */
2879
-#line 752 "pikchr.y"
2885
+#line 753 "pikchr.y"
28802886
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
2881
-#line 2906 "pikchr.c"
2887
+#line 2912 "pikchr.c"
28822888
yymsp[0].minor.yy0 = yylhsminor.yy0;
28832889
break;
28842890
case 95: /* nth ::= NTH LB RB */
2885
-#line 753 "pikchr.y"
2891
+#line 754 "pikchr.y"
28862892
{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"
28882894
yymsp[-2].minor.yy0 = yylhsminor.yy0;
28892895
break;
28902896
case 96: /* nth ::= NTH LAST LB RB */
2891
-#line 754 "pikchr.y"
2897
+#line 755 "pikchr.y"
28922898
{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"
28942900
yymsp[-3].minor.yy0 = yylhsminor.yy0;
28952901
break;
28962902
case 97: /* nth ::= LAST LB RB */
2897
-#line 755 "pikchr.y"
2903
+#line 756 "pikchr.y"
28982904
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
2899
-#line 2924 "pikchr.c"
2905
+#line 2930 "pikchr.c"
29002906
break;
29012907
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;
29062912
break;
29072913
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;
29122918
break;
29132919
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;
29182924
break;
29192925
case 101: /* expr ::= expr SLASH expr */
2920
-#line 760 "pikchr.y"
2926
+#line 761 "pikchr.y"
29212927
{
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; }
29242930
}
2925
-#line 2950 "pikchr.c"
2926
- yymsp[-2].minor.yy153 = yylhsminor.yy153;
2931
+#line 2956 "pikchr.c"
2932
+ yymsp[-2].minor.yy21 = yylhsminor.yy21;
29272933
break;
29282934
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"
29322938
break;
29332939
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"
29372943
break;
29382944
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"
29422948
break;
29432949
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"
29472953
break;
29482954
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;
29532959
break;
29542960
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;
29592965
break;
29602966
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;
29652971
break;
29662972
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;
29712977
break;
29722978
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"
29762982
break;
29772983
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;
29822988
break;
29832989
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;
29882994
break;
29892995
case 113: /* expr ::= object DOT_L numproperty */
29902996
case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
29912997
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;
29963002
break;
29973003
default:
29983004
/* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
29993005
/* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
30003006
/* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
@@ -3092,19 +3098,19 @@
30923098
){
30933099
pik_parserARG_FETCH
30943100
pik_parserCTX_FETCH
30953101
#define TOKEN yyminor
30963102
/************ Begin %syntax_error code ****************************************/
3097
-#line 535 "pikchr.y"
3103
+#line 536 "pikchr.y"
30983104
30993105
if( TOKEN.z && TOKEN.z[0] ){
31003106
pik_error(p, &TOKEN, "syntax error");
31013107
}else{
31023108
pik_error(p, 0, "syntax error");
31033109
}
31043110
UNUSED_PARAMETER(yymajor);
3105
-#line 3130 "pikchr.c"
3111
+#line 3136 "pikchr.c"
31063112
/************ End %syntax_error code ******************************************/
31073113
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
31083114
pik_parserCTX_STORE
31093115
}
31103116
@@ -3376,11 +3382,11 @@
33763382
#else
33773383
(void)iToken;
33783384
return 0;
33793385
#endif
33803386
}
3381
-#line 780 "pikchr.y"
3387
+#line 781 "pikchr.y"
33823388
33833389
33843390
33853391
/* Chart of the 148 official CSS color names with their
33863392
** corresponding RGB values thru Color Module Level 4:
@@ -4978,14 +4984,16 @@
49784984
if( t->eCode & TP_RJUST ) nx += jw;
49794985
49804986
if( pBox!=0 ){
49814987
/* If pBox is not NULL, do not draw any <text>. Instead, just expand
49824988
** 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;
49844990
PNum ch = p->charHeight*0.5*xtraFontScale;
49854991
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
+ }
49874995
if( t->eCode & TP_RJUST ){
49884996
x0 = nx;
49894997
y0 = y-ch;
49904998
x1 = nx-cw;
49914999
y1 = y+ch;
@@ -5037,10 +5045,13 @@
50375045
pik_append(p, " font-style=\"italic\"", -1);
50385046
}
50395047
if( t->eCode & TP_BOLD ){
50405048
pik_append(p, " font-weight=\"bold\"", -1);
50415049
}
5050
+ if( t->eCode & TP_MONO ){
5051
+ pik_append(p, " font-family=\"monospace\"", -1);
5052
+ }
50425053
if( pObj->color>=0.0 ){
50435054
pik_append_clr(p, " fill=\"", pObj->color, "\"",0);
50445055
}
50455056
xtraFontScale *= p->fontScale;
50465057
if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
@@ -6044,12 +6055,13 @@
60446055
case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break;
60456056
case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break;
60466057
case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break;
60476058
case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
60486059
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;
60516063
case T_ALIGNED: iRes |= TP_ALIGN; break;
60526064
case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA;
60536065
else iRes = (iRes &~TP_SZMASK)|TP_BIG; break;
60546066
case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA;
60556067
else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
@@ -6180,15 +6192,17 @@
61806192
**
61816193
** Omit "\" used to escape characters. And count entities like
61826194
** "&lt;" as a single character. Multi-byte UTF8 characters count
61836195
** as a single character.
61846196
**
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
+**
61886201
*/
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;
61906204
int n = pToken->n;
61916205
const char *z = pToken->z;
61926206
int cnt, j;
61936207
for(j=1, cnt=0; j<n-1; j++){
61946208
char c = z[j];
@@ -6196,22 +6210,24 @@
61966210
c = z[++j];
61976211
}else if( c=='&' ){
61986212
int k;
61996213
for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
62006214
if( z[k]==';' ) j = k;
6201
- cnt += 150;
6215
+ cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2;
62026216
continue;
62036217
}
62046218
if( (c & 0xc0)==0xc0 ){
62056219
while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
6206
- cnt += 100;
6220
+ cnt += isMonospace ? monoAvg : stdAvg;
62076221
continue;
62086222
}
6209
- if( c>=0x20 && c<=0x7e ){
6223
+ if( isMonospace ){
6224
+ cnt += monoAvg;
6225
+ }else if( c >= 0x20 && c <= 0x7e ){
62106226
cnt += awChar[c-0x20];
62116227
}else{
6212
- cnt += 100;
6228
+ cnt += stdAvg;
62136229
}
62146230
}
62156231
return cnt;
62166232
}
62176233
@@ -7195,10 +7211,12 @@
71957211
{ "last", 4, T_LAST, 0, 0 },
71967212
{ "left", 4, T_LEFT, DIR_LEFT, CP_W },
71977213
{ "ljust", 5, T_LJUST, 0, 0 },
71987214
{ "max", 3, T_FUNC2, FN_MAX, 0 },
71997215
{ "min", 3, T_FUNC2, FN_MIN, 0 },
7216
+ { "mono", 4, T_MONO, 0, 0 },
7217
+ { "monospace", 9, T_MONO, 0, 0 },
72007218
{ "n", 1, T_EDGEPT, 0, CP_N },
72017219
{ "ne", 2, T_EDGEPT, 0, CP_NE },
72027220
{ "north", 5, T_EDGEPT, 0, CP_N },
72037221
{ "nw", 2, T_EDGEPT, 0, CP_NW },
72047222
{ "of", 2, T_OF, 0, 0 },
@@ -8122,6 +8140,6 @@
81228140
81238141
81248142
#endif /* PIKCHR_TCL */
81258143
81268144
8127
-#line 8152 "pikchr.c"
8145
+#line 8170 "pikchr.c"
81288146
--- 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 ** "&lt;" 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 ** "&lt;" 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 @@
11
22
var initPikchrModule = (() => {
33
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
44
55
return (
6
-function(initPikchrModule) {
7
- initPikchrModule = initPikchrModule || {};
6
+function(config) {
7
+ var initPikchrModule = config || {};
88
99
var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {};
1010
1111
var readyPromiseResolve, readyPromiseReject;
1212
@@ -195,22 +195,22 @@
195195
196196
function stringToUTF8(str, outPtr, maxBytesToWrite) {
197197
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
198198
}
199199
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);
212212
}
213213
214214
var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216;
215215
216216
var wasmTable;
@@ -363,11 +363,11 @@
363363
};
364364
function receiveInstance(instance, module) {
365365
var exports = instance.exports;
366366
Module["asm"] = exports;
367367
wasmMemory = Module["asm"]["d"];
368
- updateGlobalBufferAndViews(wasmMemory.buffer);
368
+ updateMemoryViews();
369369
wasmTable = Module["asm"]["g"];
370370
addOnInit(Module["asm"]["e"]);
371371
removeRunDependency("wasm-instantiate");
372372
}
373373
addRunDependency("wasm-instantiate");
@@ -639,10 +639,12 @@
639639
};
640640
641641
var stackAlloc = Module["stackAlloc"] = function() {
642642
return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments);
643643
};
644
+
645
+Module["stackAlloc"] = stackAlloc;
644646
645647
Module["stackSave"] = stackSave;
646648
647649
Module["stackRestore"] = stackRestore;
648650
649651
--- 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
11
--- 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 @@
244244
245245
#if defined(_WIN32) || defined(WIN32)
246246
#if SQLITE_OS_WINRT
247247
#include <intrin.h>
248248
#endif
249
+#define WIN32_LEAN_AND_MEAN
249250
#include <windows.h>
250251
251252
/* string conversion routines only needed on Win32 */
252253
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
253254
extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
@@ -458,14 +459,28 @@
458459
** at an error if we are not interactive.
459460
*/
460461
static int bail_on_error = 0;
461462
462463
/*
463
-** Threat stdin as an interactive input if the following variable
464
+** Treat stdin as an interactive input if the following variable
464465
** is true. Otherwise, assume stdin is connected to a file or pipe.
465466
*/
466467
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
467482
468483
/*
469484
** On Windows systems we have to know if standard output is a console
470485
** in order to translate UTF-8 into MBCS. The following variable is
471486
** true if translation is required.
@@ -595,20 +610,154 @@
595610
}
596611
return dynPrompt.dynamicPrompt;
597612
}
598613
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
599614
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
+
600743
/*
601744
** 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.)
604749
*/
605750
#if defined(_WIN32) || defined(WIN32)
606751
void utf8_printf(FILE *out, const char *zFormat, ...){
607752
va_list ap;
608753
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
+ ){
610759
char *z1 = sqlite3_vmprintf(zFormat, ap);
611760
char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0);
612761
sqlite3_free(z1);
613762
fputs(z2, out);
614763
sqlite3_free(z2);
@@ -636,11 +785,11 @@
636785
}
637786
638787
/* Check a pointer to see if it is NULL. If it is NULL, exit with an
639788
** out-of-memory error.
640789
*/
641
-static void shell_check_oom(void *p){
790
+static void shell_check_oom(const void *p){
642791
if( p==0 ) shell_out_of_memory();
643792
}
644793
645794
/*
646795
** Write I/O traces to the following stream.
@@ -815,13 +964,18 @@
815964
zLine[n] = 0;
816965
break;
817966
}
818967
}
819968
#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
+ ){
823977
char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
824978
if( zTrans ){
825979
i64 nTrans = strlen(zTrans)+1;
826980
if( nTrans>nLine ){
827981
zLine = realloc(zLine, nTrans);
@@ -858,14 +1012,25 @@
8581012
}else{
8591013
zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
8601014
#if SHELL_USE_LOCAL_GETLINE
8611015
printf("%s", zPrompt);
8621016
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 );
8641023
#else
8651024
free(zPrior);
8661025
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
+ }
8671032
if( zResult && *zResult ) shell_add_history(zResult);
8681033
#endif
8691034
}
8701035
return zResult;
8711036
}
@@ -1001,10 +1166,11 @@
10011166
**
10021167
** Return '"' if quoting is required. Return 0 if no quoting is required.
10031168
*/
10041169
static char quoteChar(const char *zName){
10051170
int i;
1171
+ if( zName==0 ) return '"';
10061172
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
10071173
for(i=0; zName[i]; i++){
10081174
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
10091175
}
10101176
return sqlite3_keyword_check(zName, i) ? '"' : 0;
@@ -1644,11 +1810,12 @@
16441810
** May you find forgiveness for yourself and forgive others.
16451811
** May you share freely, never taking more than you give.
16461812
**
16471813
******************************************************************************
16481814
**
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.
16501817
** Two SQL functions are implemented:
16511818
**
16521819
** sha3(X,SIZE)
16531820
** sha3_query(Y,SIZE)
16541821
**
@@ -3244,22 +3411,22 @@
32443411
*pOut = 0;
32453412
return pOut;
32463413
}
32473414
32483415
/* Skip over text which is not base64 numeral(s). */
3249
-static char * skipNonB64( char *s ){
3416
+static char * skipNonB64( char *s, int nc ){
32503417
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;
32523419
return s;
32533420
}
32543421
32553422
/* Decode base64 text into a byte buffer. */
32563423
static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){
32573424
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
32583425
while( ncIn>0 && *pIn!=PAD_CHAR ){
32593426
static signed char nboi[] = { 0, 0, 1, 2, 3 };
3260
- char *pUse = skipNonB64(pIn);
3427
+ char *pUse = skipNonB64(pIn, ncIn);
32613428
unsigned long qv = 0L;
32623429
int nti, nbo, nac;
32633430
ncIn -= (pUse - pIn);
32643431
pIn = pUse;
32653432
nti = (ncIn>4)? 4 : ncIn;
@@ -3314,14 +3481,21 @@
33143481
nc = 4*(nv+2/3); /* quads needed */
33153482
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
33163483
if( nvMax < nc ){
33173484
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
33183485
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;
33193494
}
33203495
cBuf = sqlite3_malloc(nc);
33213496
if( !cBuf ) goto memFail;
3322
- bBuf = (u8*)sqlite3_value_blob(av[0]);
33233497
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
33243498
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
33253499
break;
33263500
case SQLITE_TEXT:
33273501
nc = nv;
@@ -3329,14 +3503,21 @@
33293503
if( nvMax < nb ){
33303504
sqlite3_result_error(context, "blob from base64 may be too big", -1);
33313505
return;
33323506
}else if( nb<1 ){
33333507
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;
33343516
}
33353517
bBuf = sqlite3_malloc(nb);
33363518
if( !bBuf ) goto memFail;
3337
- cBuf = (char *)sqlite3_value_text(av[0]);
33383519
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
33393520
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
33403521
break;
33413522
default:
33423523
sqlite3_result_error(context, "base64 accepts only blob or text", -1);
@@ -3520,13 +3701,13 @@
35203701
35213702
/* Width of base64 lines. Should be an integer multiple of 5. */
35223703
#define B85_DARK_MAX 80
35233704
35243705
3525
-static char * skipNonB85( char *s ){
3706
+static char * skipNonB85( char *s, int nc ){
35263707
char c;
3527
- while( (c = *s) && !IS_B85(c) ) ++s;
3708
+ while( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s;
35283709
return s;
35293710
}
35303711
35313712
/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.
35323713
* Do not use the macro form with argument expression having a side-effect.*/
@@ -3592,11 +3773,11 @@
35923773
/* Decode base85 text into a byte buffer. */
35933774
static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){
35943775
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
35953776
while( ncIn>0 ){
35963777
static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
3597
- char *pUse = skipNonB85(pIn);
3778
+ char *pUse = skipNonB85(pIn, ncIn);
35983779
unsigned long qv = 0L;
35993780
int nti, nbo;
36003781
ncIn -= (pUse - pIn);
36013782
pIn = pUse;
36023783
nti = (ncIn>5)? 5 : ncIn;
@@ -3676,14 +3857,21 @@
36763857
/* ulongs tail newlines tailenc+nul*/
36773858
nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
36783859
if( nvMax < nc ){
36793860
sqlite3_result_error(context, "blob expanded to base85 too big", -1);
36803861
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;
36813870
}
36823871
cBuf = sqlite3_malloc(nc);
36833872
if( !cBuf ) goto memFail;
3684
- bBuf = (u8*)sqlite3_value_blob(av[0]);
36853873
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
36863874
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
36873875
break;
36883876
case SQLITE_TEXT:
36893877
nc = nv;
@@ -3691,14 +3879,21 @@
36913879
if( nvMax < nb ){
36923880
sqlite3_result_error(context, "blob from base85 may be too big", -1);
36933881
return;
36943882
}else if( nb<1 ){
36953883
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;
36963892
}
36973893
bBuf = sqlite3_malloc(nb);
36983894
if( !bBuf ) goto memFail;
3699
- cBuf = (char *)sqlite3_value_text(av[0]);
37003895
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
37013896
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
37023897
break;
37033898
default:
37043899
sqlite3_result_error(context, "base85 accepts only blob or text.", -1);
@@ -4114,11 +4309,11 @@
41144309
}
41154310
41164311
/************************* End ../ext/misc/ieee754.c ********************/
41174312
/************************* Begin ../ext/misc/series.c ******************/
41184313
/*
4119
-** 2015-08-18
4314
+** 2015-08-18, 2023-04-28
41204315
**
41214316
** The author disclaims copyright to this source code. In place of
41224317
** a legal notice, here is a blessing:
41234318
**
41244319
** May you do good and not evil.
@@ -4127,11 +4322,23 @@
41274322
**
41284323
*************************************************************************
41294324
**
41304325
** This file demonstrates how to create a table-valued-function using
41314326
** 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
+**
41334340
** Examples:
41344341
**
41354342
** SELECT * FROM generate_series(0,100,5);
41364343
**
41374344
** The query above returns integers from 0 through 100 counting by steps
@@ -4143,10 +4350,18 @@
41434350
**
41444351
** SELECT * FROM generate_series(20) LIMIT 10;
41454352
**
41464353
** Integers 20 through 29.
41474354
**
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
+**
41484363
** HOW IT WORKS
41494364
**
41504365
** The generate_series "function" is really a virtual table with the
41514366
** following schema:
41524367
**
@@ -4154,10 +4369,13 @@
41544369
** value,
41554370
** start HIDDEN,
41564371
** stop HIDDEN,
41574372
** step HIDDEN
41584373
** );
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.
41594377
**
41604378
** Function arguments in queries against this virtual table are translated
41614379
** into equality constraints against successive hidden columns. In other
41624380
** words, the following pairs of queries are equivalent to each other:
41634381
**
@@ -4187,27 +4405,139 @@
41874405
*/
41884406
/* #include "sqlite3ext.h" */
41894407
SQLITE_EXTENSION_INIT1
41904408
#include <assert.h>
41914409
#include <string.h>
4410
+#include <limits.h>
41924411
41934412
#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
+}
41944438
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
+}
41954530
41964531
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
41974532
** serve as the underlying representation of a cursor that scans
41984533
** over rows of the result
41994534
*/
42004535
typedef struct series_cursor series_cursor;
42014536
struct series_cursor {
42024537
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 */
42094539
};
42104540
42114541
/*
42124542
** The seriesConnect() method is invoked to create a new
42134543
** series_vtab that describes the generate_series virtual table.
@@ -4285,16 +4615,11 @@
42854615
/*
42864616
** Advance a series_cursor to its next row of output.
42874617
*/
42884618
static int seriesNext(sqlite3_vtab_cursor *cur){
42894619
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 );
42964621
return SQLITE_OK;
42974622
}
42984623
42994624
/*
43004625
** Return values of columns for the row at which the series_cursor
@@ -4306,44 +4631,40 @@
43064631
int i /* Which column to return */
43074632
){
43084633
series_cursor *pCur = (series_cursor*)cur;
43094634
sqlite3_int64 x = 0;
43104635
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;
43154640
}
43164641
sqlite3_result_int64(ctx, x);
43174642
return SQLITE_OK;
43184643
}
43194644
43204645
/*
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.
43244648
*/
43254649
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
43264650
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);
43284653
return SQLITE_OK;
43294654
}
43304655
43314656
/*
43324657
** Return TRUE if the cursor has been moved off of the last
43334658
** row of output.
43344659
*/
43354660
static int seriesEof(sqlite3_vtab_cursor *cur){
43364661
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;
43424663
}
43434664
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=
43454666
** parameters. The only reason to do this is for testing the
43464667
** constraint checking logic for virtual tables in the SQLite core.
43474668
*/
43484669
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
43494670
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
@@ -4350,11 +4671,11 @@
43504671
#endif
43514672
43524673
/*
43534674
** This method is called to "rewind" the series_cursor object back
43544675
** 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
43564677
** seriesEof().
43574678
**
43584679
** The query plan selected by seriesBestIndex is passed in the idxNum
43594680
** parameter. (idxStr is not used in this implementation.) idxNum
43604681
** is a bitmask showing which constraints are available:
@@ -4370,58 +4691,53 @@
43704691
** This routine should initialize the cursor and position it so that it
43714692
** is pointing at the first row, or pointing off the end of the table
43724693
** (so that seriesEof() will return true) if the table is empty.
43734694
*/
43744695
static int seriesFilter(
4375
- sqlite3_vtab_cursor *pVtabCursor,
4696
+ sqlite3_vtab_cursor *pVtabCursor,
43764697
int idxNum, const char *idxStrUnused,
43774698
int argc, sqlite3_value **argv
43784699
){
43794700
series_cursor *pCur = (series_cursor *)pVtabCursor;
43804701
int i = 0;
43814702
(void)idxStrUnused;
43824703
if( idxNum & 1 ){
4383
- pCur->mnValue = sqlite3_value_int64(argv[i++]);
4704
+ pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
43844705
}else{
4385
- pCur->mnValue = 0;
4706
+ pCur->ss.iBase = 0;
43864707
}
43874708
if( idxNum & 2 ){
4388
- pCur->mxValue = sqlite3_value_int64(argv[i++]);
4709
+ pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
43894710
}else{
4390
- pCur->mxValue = 0xffffffff;
4711
+ pCur->ss.iTerm = 0xffffffff;
43914712
}
43924713
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 ){
43984718
if( (idxNum & 16)==0 ) idxNum |= 8;
43994719
}
44004720
}else{
4401
- pCur->iStep = 1;
4721
+ pCur->ss.iStep = 1;
44024722
}
44034723
for(i=0; i<argc; i++){
44044724
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
44054725
/* If any of the constraints have a NULL value, then return no rows.
44064726
** 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;
44094730
break;
44104731
}
44114732
}
44124733
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;
44184735
}else{
4419
- pCur->isDesc = 0;
4420
- pCur->iValue = pCur->mnValue;
4736
+ pCur->ss.isReversing = pCur->ss.iStep < 0;
44214737
}
4422
- pCur->iRowid = 1;
4738
+ setupSequence( &pCur->ss );
44234739
return SQLITE_OK;
44244740
}
44254741
44264742
/*
44274743
** SQLite will invoke this method one or more times while planning a query
@@ -5172,11 +5488,11 @@
51725488
re_append(p, RE_OP_FORK, -sz);
51735489
}
51745490
break;
51755491
}
51765492
case '[': {
5177
- int iFirst = p->nState;
5493
+ unsigned int iFirst = p->nState;
51785494
if( rePeek(p)=='^' ){
51795495
re_append(p, RE_OP_CC_EXC, 0);
51805496
p->sIn.i++;
51815497
}else{
51825498
re_append(p, RE_OP_CC_INC, 0);
@@ -5196,11 +5512,11 @@
51965512
re_append(p, RE_OP_CC_VALUE, c);
51975513
}
51985514
if( rePeek(p)==']' ){ p->sIn.i++; break; }
51995515
}
52005516
if( c==0 ) return "unclosed '['";
5201
- p->aArg[iFirst] = p->nState - iFirst;
5517
+ if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
52025518
break;
52035519
}
52045520
case '\\': {
52055521
int specialOp = 0;
52065522
switch( rePeek(p) ){
@@ -9213,13 +9529,23 @@
92139529
** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
92149530
** linked list. Remove it from the list and free the object.
92159531
*/
92169532
static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
92179533
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
+ }
92219547
zipfileEntryFree(pOld);
92229548
}
92239549
}
92249550
92259551
/*
@@ -12510,10 +12836,11 @@
1251012836
#endif
1251112837
1251212838
#endif /* ifndef _SQLITE_RECOVER_H */
1251312839
1251412840
/************************* End ../ext/recover/sqlite3recover.h ********************/
12841
+# ifndef SQLITE_HAVE_SQLITE3R
1251512842
/************************* Begin ../ext/recover/dbdata.c ******************/
1251612843
/*
1251712844
** 2019-04-17
1251812845
**
1251912846
** The author disclaims copyright to this source code. In place of
@@ -14576,11 +14903,11 @@
1457614903
pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName);
1457714904
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
1457814905
int iField = sqlite3_column_int(pStmt, 0);
1457914906
int iCol = sqlite3_column_int(pStmt, 1);
1458014907
14581
- assert( iField<pNew->nCol && iCol<pNew->nCol );
14908
+ assert( iCol<pNew->nCol );
1458214909
pNew->aCol[iCol].iField = iField;
1458314910
1458414911
pNew->bIntkey = 0;
1458514912
iPk = -2;
1458614913
}
@@ -16340,10 +16667,11 @@
1634016667
}
1634116668
1634216669
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
1634316670
1634416671
/************************* End ../ext/recover/sqlite3recover.c ********************/
16672
+# endif /* SQLITE_HAVE_SQLITE3R */
1634516673
#endif
1634616674
#ifdef SQLITE_SHELL_EXTSRC
1634716675
# include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC)
1634816676
#endif
1634916677
@@ -16521,10 +16849,11 @@
1652116849
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
1652216850
#define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */
1652316851
#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */
1652416852
#define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */
1652516853
#define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */
16854
+#define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */
1652616855
1652716856
/*
1652816857
** Macros for testing and setting shellFlgs
1652916858
*/
1653016859
#define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0)
@@ -16764,10 +17093,11 @@
1676417093
if( hasCRNL ){
1676517094
/* If the original contains \r\n then do no conversions back to \n */
1676617095
}else{
1676717096
/* If the file did not originally contain \r\n then convert any new
1676817097
** \r\n back into \n */
17098
+ p[sz] = 0;
1676917099
for(i=j=0; i<sz; i++){
1677017100
if( p[i]=='\r' && p[i+1]=='\n' ) i++;
1677117101
p[j++] = p[i];
1677217102
}
1677317103
sz = j;
@@ -16854,10 +17184,11 @@
1685417184
*/
1685517185
static void output_quoted_string(FILE *out, const char *z){
1685617186
int i;
1685717187
char c;
1685817188
setBinaryMode(out, 1);
17189
+ if( z==0 ) return;
1685917190
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
1686017191
if( c==0 ){
1686117192
utf8_printf(out,"'%s'",z);
1686217193
}else{
1686317194
raw_printf(out, "'");
@@ -16983,10 +17314,11 @@
1698317314
/*
1698417315
** Output the given string as a quoted according to JSON quoting rules.
1698517316
*/
1698617317
static void output_json_string(FILE *out, const char *z, i64 n){
1698717318
unsigned int c;
17319
+ if( z==0 ) z = "";
1698817320
if( n<0 ) n = strlen(z);
1698917321
fputc('"', out);
1699017322
while( n-- ){
1699117323
c = *(z++);
1699217324
if( c=='\\' || c=='"' ){
@@ -17107,12 +17439,11 @@
1710717439
/*
1710817440
** This routine runs when the user presses Ctrl-C
1710917441
*/
1711017442
static void interrupt_handler(int NotUsed){
1711117443
UNUSED_PARAMETER(NotUsed);
17112
- seenInterrupt++;
17113
- if( seenInterrupt>2 ) exit(1);
17444
+ if( ++seenInterrupt>1 ) exit(1);
1711417445
if( globalDb ) sqlite3_interrupt(globalDb);
1711517446
}
1711617447
1711717448
#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
1711817449
/*
@@ -18559,14 +18890,18 @@
1855918890
zVar = zNum;
1856018891
}
1856118892
sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC);
1856218893
if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){
1856318894
sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0));
18895
+#ifdef NAN
1856418896
}else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){
1856518897
sqlite3_bind_double(pStmt, i, NAN);
18898
+#endif
18899
+#ifdef INFINITY
1856618900
}else if( sqlite3_strlike("_INF", zVar, 0)==0 ){
1856718901
sqlite3_bind_double(pStmt, i, INFINITY);
18902
+#endif
1856818903
}else{
1856918904
sqlite3_bind_null(pStmt, i);
1857018905
}
1857118906
sqlite3_reset(pQ);
1857218907
}
@@ -18806,11 +19141,11 @@
1880619141
nAlloc = nColumn*4;
1880719142
if( nAlloc<=0 ) nAlloc = 1;
1880819143
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
1880919144
shell_check_oom(azData);
1881019145
azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
18811
- shell_check_oom((void*)azNextLine);
19146
+ shell_check_oom(azNextLine);
1881219147
memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
1881319148
if( p->cmOpts.bQuote ){
1881419149
azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
1881519150
shell_check_oom(azQuoted);
1881619151
memset(azQuoted, 0, nColumn*sizeof(char*) );
@@ -18836,10 +19171,11 @@
1883619171
if( wx==0 ){
1883719172
wx = p->cmOpts.iWrap;
1883819173
}
1883919174
if( wx<0 ) wx = -wx;
1884019175
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
19176
+ if( uz==0 ) uz = (u8*)"";
1884119177
azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
1884219178
}
1884319179
do{
1884419180
int useNextLine = bNextLine;
1884519181
bNextLine = 0;
@@ -19769,17 +20105,17 @@
1976920105
" from the \".mode\" output mode",
1977020106
" * If FILE begins with \"|\" then it is a command that generates the",
1977120107
" input text.",
1977220108
#endif
1977320109
#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",
1977520111
#endif
1977620112
".indexes ?TABLE? Show names of indexes",
1977720113
" If TABLE is specified, only show indexes for",
1977820114
" tables matching TABLE using the LIKE operator.",
1977920115
#ifdef SQLITE_ENABLE_IOTRACE
19780
- ".iotrace FILE Enable I/O diagnostic logging to FILE",
20116
+ ",iotrace FILE Enable I/O diagnostic logging to FILE",
1978120117
#endif
1978220118
".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT",
1978320119
".lint OPTIONS Report potential schema issues.",
1978420120
" Options:",
1978520121
" fkey-indexes Find missing foreign key indexes",
@@ -19884,11 +20220,11 @@
1988420220
".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off",
1988520221
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
1988620222
" Options:",
1988720223
" --indent Try to pretty-print the schema",
1988820224
" --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",
1989020226
" Options:",
1989120227
" --init Create a new SELFTEST table",
1989220228
" -v Verbose output",
1989320229
".separator COL ?ROW? Change the column and row separators",
1989420230
#if defined(SQLITE_ENABLE_SESSION)
@@ -19926,13 +20262,13 @@
1992620262
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE)
1992720263
".system CMD ARGS... Run CMD ARGS... in a system shell",
1992820264
#endif
1992920265
".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
1993020266
#ifndef SQLITE_SHELL_FIDDLE
19931
- ".testcase NAME Begin redirecting output to 'testcase-out.txt'",
20267
+ ",testcase NAME Begin redirecting output to 'testcase-out.txt'",
1993220268
#endif
19933
- ".testctrl CMD ... Run various sqlite3_test_control() operations",
20269
+ ",testctrl CMD ... Run various sqlite3_test_control() operations",
1993420270
" Run \".testctrl\" with no arguments for details",
1993520271
".timeout MS Try opening locked tables for MS milliseconds",
1993620272
".timer on|off Turn SQL timer on or off",
1993720273
#ifndef SQLITE_OMIT_TRACE
1993820274
".trace ?OPTIONS? Output each SQL statement as it is run",
@@ -19980,20 +20316,45 @@
1998020316
|| zPattern[0]=='0'
1998120317
|| cli_strcmp(zPattern,"-a")==0
1998220318
|| cli_strcmp(zPattern,"-all")==0
1998320319
|| cli_strcmp(zPattern,"--all")==0
1998420320
){
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
+ }
1998720333
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
+ }
1999120352
}
1999220353
}
1999320354
}else{
19994
- /* Look for commands that for which zPattern is an exact prefix */
20355
+ /* Seek documented commands for which zPattern is an exact prefix */
1999520356
zPat = sqlite3_mprintf(".%s*", zPattern);
1999620357
shell_check_oom(zPat);
1999720358
for(i=0; i<ArraySize(azHelp); i++){
1999820359
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
1999920360
utf8_printf(out, "%s\n", azHelp[i]);
@@ -20002,28 +20363,32 @@
2000220363
}
2000320364
}
2000420365
sqlite3_free(zPat);
2000520366
if( n ){
2000620367
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]==' ' ){
2001020371
utf8_printf(out, "%s\n", azHelp[j]);
2001120372
j++;
2001220373
}
2001320374
}
2001420375
return n;
2001520376
}
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. */
2001820379
zPat = sqlite3_mprintf("%%%s%%", zPattern);
2001920380
shell_check_oom(zPat);
2002020381
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
+ }
2002120386
if( azHelp[i][0]=='.' ) j = i;
2002220387
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
2002320388
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]==' ' ){
2002520390
j++;
2002620391
utf8_printf(out, "%s\n", azHelp[j]);
2002720392
}
2002820393
i = j;
2002920394
n++;
@@ -20337,17 +20702,34 @@
2033720702
}
2033820703
globalDb = p->db;
2033920704
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
2034020705
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
2034120706
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);
2034520709
}
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
+ }
2034720722
}
2034820723
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
+ }
2034920731
2035020732
#ifndef SQLITE_OMIT_LOAD_EXTENSION
2035120733
sqlite3_enable_load_extension(p->db, 1);
2035220734
#endif
2035320735
sqlite3_shathree_init(p->db, 0, 0);
@@ -20356,13 +20738,10 @@
2035620738
sqlite3_base64_init(p->db, 0, 0);
2035720739
sqlite3_base85_init(p->db, 0, 0);
2035820740
sqlite3_regexp_init(p->db, 0, 0);
2035920741
sqlite3_ieee_init(p->db, 0, 0);
2036020742
sqlite3_series_init(p->db, 0, 0);
20361
-#if SQLITE_SHELL_HAVE_RECOVER
20362
- sqlite3_dbdata_init(p->db, 0, 0);
20363
-#endif
2036420743
#ifndef SQLITE_SHELL_FIDDLE
2036520744
sqlite3_fileio_init(p->db, 0, 0);
2036620745
sqlite3_completion_init(p->db, 0, 0);
2036720746
#endif
2036820747
#ifdef SQLITE_HAVE_ZLIB
@@ -20550,10 +20929,11 @@
2055020929
** \s -> space
2055120930
** \" -> "
2055220931
** \' -> '
2055320932
** \\ -> backslash
2055420933
** \NNN -> ascii character NNN in octal
20934
+** \xHH -> ascii character HH in hexadecimal
2055520935
*/
2055620936
static void resolve_backslashes(char *z){
2055720937
int i, j;
2055820938
char c;
2055920939
while( *z && *z!='\\' ) z++;
@@ -20578,10 +20958,19 @@
2057820958
c = '"';
2057920959
}else if( c=='\'' ){
2058020960
c = '\'';
2058120961
}else if( c=='\\' ){
2058220962
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;
2058320972
}else if( c>='0' && c<='7' ){
2058420973
c -= '0';
2058520974
if( z[i+1]>='0' && z[i+1]<='7' ){
2058620975
i++;
2058720976
c = (c<<3) + z[i] - '0';
@@ -20677,11 +21066,11 @@
2067721066
if( p->traceOut==0 ) return 0;
2067821067
if( mType==SQLITE_TRACE_CLOSE ){
2067921068
utf8_printf(p->traceOut, "-- closing database connection\n");
2068021069
return 0;
2068121070
}
20682
- if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){
21071
+ if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){
2068321072
zSql = (const char*)pX;
2068421073
}else{
2068521074
pStmt = (sqlite3_stmt*)pP;
2068621075
switch( p->eTraceType ){
2068721076
case SHELL_TRACE_EXPANDED: {
@@ -20709,11 +21098,11 @@
2070921098
case SQLITE_TRACE_STMT: {
2071021099
utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
2071121100
break;
2071221101
}
2071321102
case SQLITE_TRACE_PROFILE: {
20714
- sqlite3_int64 nNanosec = *(sqlite3_int64*)pX;
21103
+ sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
2071521104
utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
2071621105
break;
2071721106
}
2071821107
}
2071921108
return 0;
@@ -20785,12 +21174,12 @@
2078521174
** EOF on end-of-file.
2078621175
** + Report syntax errors on stderr
2078721176
*/
2078821177
static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
2078921178
int c;
20790
- int cSep = p->cColSep;
20791
- int rSep = p->cRowSep;
21179
+ int cSep = (u8)p->cColSep;
21180
+ int rSep = (u8)p->cRowSep;
2079221181
p->n = 0;
2079321182
c = fgetc(p->in);
2079421183
if( c==EOF || seenInterrupt ){
2079521184
p->cTerm = EOF;
2079621185
return 0;
@@ -20875,12 +21264,12 @@
2087521264
** EOF on end-of-file.
2087621265
** + Report syntax errors on stderr
2087721266
*/
2087821267
static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
2087921268
int c;
20880
- int cSep = p->cColSep;
20881
- int rSep = p->cRowSep;
21269
+ int cSep = (u8)p->cColSep;
21270
+ int rSep = (u8)p->cRowSep;
2088221271
p->n = 0;
2088321272
c = fgetc(p->in);
2088421273
if( c==EOF || seenInterrupt ){
2088521274
p->cTerm = EOF;
2088621275
return 0;
@@ -21070,11 +21459,11 @@
2107021459
}
2107121460
while( sqlite3_step(pQuery)==SQLITE_ROW ){
2107221461
zName = sqlite3_column_text(pQuery, 0);
2107321462
zSql = sqlite3_column_text(pQuery, 1);
2107421463
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;
2107621465
printf("%s... ", zName); fflush(stdout);
2107721466
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
2107821467
if( zErrMsg ){
2107921468
utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
2108021469
sqlite3_free(zErrMsg);
@@ -21237,11 +21626,13 @@
2123721626
}
2123821627
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
2123921628
if( sqlite3_step(pStmt)==SQLITE_ROW
2124021629
&& sqlite3_column_bytes(pStmt,0)>100
2124121630
){
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);
2124321634
sqlite3_finalize(pStmt);
2124421635
}else{
2124521636
raw_printf(stderr, "unable to read database header\n");
2124621637
sqlite3_finalize(pStmt);
2124721638
return 1;
@@ -22054,11 +22445,14 @@
2205422445
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
2205522446
}
2205622447
}
2205722448
}
2205822449
}
22059
-
22450
+ if( pAr->eCmd==0 ){
22451
+ utf8_printf(stderr, "Required argument missing. Usage:\n");
22452
+ return arUsage(stderr);
22453
+ }
2206022454
return SQLITE_OK;
2206122455
}
2206222456
2206322457
/*
2206422458
** This function assumes that all arguments within the ArCommand.azArg[]
@@ -22820,11 +23214,11 @@
2282023214
rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
2282123215
rc_err_oom_die(rc);
2282223216
sqlite3_bind_int(pStmt, 1, nDigits);
2282323217
rc = sqlite3_step(pStmt);
2282423218
sqlite3_finalize(pStmt);
22825
- assert(rc==SQLITE_DONE);
23219
+ if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
2282623220
}
2282723221
assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
2282823222
rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
2282923223
rc_err_oom_die(rc);
2283023224
rc = sqlite3_step(pStmt);
@@ -23752,12 +24146,12 @@
2375224146
if( nSep>1 ){
2375324147
raw_printf(stderr, "Error: multi-character row separators not allowed"
2375424148
" for import\n");
2375524149
goto meta_command_exit;
2375624150
}
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];
2375924153
}
2376024154
sCtx.zFile = zFile;
2376124155
sCtx.nLine = 1;
2376224156
if( sCtx.zFile[0]=='|' ){
2376324157
#ifdef SQLITE_OMIT_POPEN
@@ -23949,10 +24343,16 @@
2394924343
sqlite3_stmt *pStmt;
2395024344
int tnum = 0;
2395124345
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
2395224346
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
2395324347
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
+ }
2395424354
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
2395524355
utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
2395624356
" .imposter off\n");
2395724357
/* Also allowed, but not documented:
2395824358
**
@@ -24133,11 +24533,12 @@
2413324533
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE)
2413424534
if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){
2413524535
const char *zFile, *zProc;
2413624536
char *zErrMsg = 0;
2413724537
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.) */
2413924540
raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
2414024541
rc = 1;
2414124542
goto meta_command_exit;
2414224543
}
2414324544
zFile = azArg[1];
@@ -25457,32 +25858,40 @@
2545725858
"' OR ') as query, tname from tabcols group by tname)"
2545825859
, zRevText);
2545925860
shell_check_oom(zRevText);
2546025861
if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
2546125862
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");
2548425893
sqlite3_free(zRevText);
2548525894
}
2548625895
#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
2548725896
sqlite3_free(zSql);
2548825897
}else
@@ -25741,10 +26150,16 @@
2574126150
int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
2574226151
int isOk = 0;
2574326152
int i, n2;
2574426153
const char *zCmd = 0;
2574526154
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
+ }
2574626161
open_db(p, 0);
2574726162
zCmd = nArg>=2 ? azArg[1] : "help";
2574826163
2574926164
/* The argument can optionally begin with "-" or "--" */
2575026165
if( zCmd[0]=='-' && zCmd[1] ){
@@ -26740,10 +27155,14 @@
2674027155
" -sorterref SIZE sorter references threshold size\n"
2674127156
#endif
2674227157
" -stats print memory stats before each finalize\n"
2674327158
" -table set output mode to 'table'\n"
2674427159
" -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
2674527164
" -version show SQLite version\n"
2674627165
" -vfs NAME use NAME as the default VFS\n"
2674727166
#ifdef SQLITE_ENABLE_VFSTRACE
2674827167
" -vfstrace enable tracing of all VFS calls\n"
2674927168
#endif
@@ -26831,10 +27250,14 @@
2683127250
argv[0], argv[argc-1]);
2683227251
exit(1);
2683327252
}
2683427253
return argv[i];
2683527254
}
27255
+
27256
+static void sayAbnormalExit(void){
27257
+ if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
27258
+}
2683627259
2683727260
#ifndef SQLITE_SHELL_IS_UTF8
2683827261
# if (defined(_WIN32) || defined(WIN32)) \
2683927262
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
2684027263
# define SQLITE_SHELL_IS_UTF8 (0)
@@ -26852,11 +27275,11 @@
2685227275
#else
2685327276
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
2685427277
char **argv;
2685527278
#endif
2685627279
#ifdef SQLITE_DEBUG
26857
- sqlite3_int64 mem_main_enter = sqlite3_memory_used();
27280
+ sqlite3_int64 mem_main_enter = 0;
2685827281
#endif
2685927282
char *zErrMsg = 0;
2686027283
#ifdef SQLITE_SHELL_FIDDLE
2686127284
# define data shellState
2686227285
#else
@@ -26873,22 +27296,27 @@
2687327296
const char *zVfs = 0; /* Value of -vfs command-line option */
2687427297
#if !SQLITE_SHELL_IS_UTF8
2687527298
char **argvToFree = 0;
2687627299
int argcToFree = 0;
2687727300
#endif
26878
-
26879
- setBinaryMode(stdin, 0);
2688027301
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
27302
+
2688127303
#ifdef SQLITE_SHELL_FIDDLE
2688227304
stdin_is_interactive = 0;
2688327305
stdout_is_console = 1;
2688427306
data.wasm.zDefaultDbName = "/fiddle.sqlite3";
2688527307
#else
2688627308
stdin_is_interactive = isatty(0);
2688727309
stdout_is_console = isatty(1);
2688827310
#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
2689027318
#if !defined(_WIN32_WCE)
2689127319
if( getenv("SQLITE_DEBUG_BREAK") ){
2689227320
if( isatty(0) && isatty(2) ){
2689327321
fprintf(stderr,
2689427322
"attach debugger to process %d and press any key to continue.\n",
@@ -26904,10 +27332,18 @@
2690427332
#elif defined(SIGTRAP)
2690527333
raise(SIGTRAP);
2690627334
#endif
2690727335
}
2690827336
}
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
+ }
2690927345
#endif
2691027346
2691127347
#if USE_SYSTEM_SQLITE+0!=1
2691227348
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
2691327349
utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
@@ -26944,19 +27380,10 @@
2694427380
#endif
2694527381
2694627382
assert( argc>=1 && argv && argv[0] );
2694727383
Argv0 = argv[0];
2694827384
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
-
2695827385
#ifdef SQLITE_SHELL_DBNAME_PROC
2695927386
{
2696027387
/* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
2696127388
** of a C-function that will provide the name of the database file. Use
2696227389
** this compile-time option to embed this shell program in larger
@@ -27109,10 +27536,12 @@
2710927536
}else if( cli_strcmp(z,"-bail")==0 ){
2711027537
bail_on_error = 1;
2711127538
}else if( cli_strcmp(z,"-nonce")==0 ){
2711227539
free(data.zNonce);
2711327540
data.zNonce = strdup(argv[++i]);
27541
+ }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
27542
+ ShellSetFlag(&data,SHFLG_TestingMode);
2711427543
}else if( cli_strcmp(z,"-safe")==0 ){
2711527544
/* no-op - catch this on the second pass */
2711627545
}
2711727546
}
2711827547
#ifndef SQLITE_SHELL_FIDDLE
@@ -27271,10 +27700,14 @@
2727127700
return 0;
2727227701
}else if( cli_strcmp(z,"-interactive")==0 ){
2727327702
stdin_is_interactive = 1;
2727427703
}else if( cli_strcmp(z,"-batch")==0 ){
2727527704
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 */
2727627709
}else if( cli_strcmp(z,"-heap")==0 ){
2727727710
i++;
2727827711
}else if( cli_strcmp(z,"-pagecache")==0 ){
2727927712
i+=2;
2728027713
}else if( cli_strcmp(z,"-lookaside")==0 ){
@@ -27341,17 +27774,27 @@
2734127774
readStdin = 0;
2734227775
break;
2734327776
#endif
2734427777
}else if( cli_strcmp(z,"-safe")==0 ){
2734527778
data.bSafeMode = data.bSafeModePersist = 1;
27779
+ }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
27780
+ /* Acted upon in first pass. */
2734627781
}else{
2734727782
utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
2734827783
raw_printf(stderr,"Use -help for a list of options.\n");
2734927784
return 1;
2735027785
}
2735127786
data.cMode = data.mode;
2735227787
}
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
2735327796
2735427797
if( !readStdin ){
2735527798
/* Run all arguments that do not begin with '-' as if they were separate
2735627799
** command-line inputs, except for the argToSkip argument which contains
2735727800
** the database filename.
2735827801
--- 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 @@
121121
#if defined(_MSC_VER) && !defined(_WIN64)
122122
#undef SQLITE_4_BYTE_ALIGNED_MALLOC
123123
#define SQLITE_4_BYTE_ALIGNED_MALLOC
124124
#endif /* defined(_MSC_VER) && !defined(_WIN64) */
125125
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
+
126130
#endif /* SQLITE_MSVC_H */
127131
128132
/************** End of msvc.h ************************************************/
129133
/************** Continuing where we left off in sqliteInt.h ******************/
130134
@@ -452,11 +456,11 @@
452456
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
453457
** [sqlite_version()] and [sqlite_source_id()].
454458
*/
455459
#define SQLITE_VERSION "3.42.0"
456460
#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"
458462
459463
/*
460464
** CAPI3REF: Run-Time Library Version Numbers
461465
** KEYWORDS: sqlite3_version sqlite3_sourceid
462466
**
@@ -2446,32 +2450,32 @@
24462450
** configuration setting is never used, then the default maximum is determined
24472451
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
24482452
** compile-time option is not set, then the default maximum is 1073741824.
24492453
** </dl>
24502454
*/
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* */
24702474
#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 */
24732477
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
24742478
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
24752479
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
24762480
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
24772481
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
@@ -2702,29 +2706,29 @@
27022706
** additional information. This feature can also be turned on and off
27032707
** using the [PRAGMA legacy_alter_table] statement.
27042708
** </dd>
27052709
**
27062710
** [[SQLITE_DBCONFIG_DQS_DML]]
2707
-** <dt>SQLITE_DBCONFIG_DQS_DML</td>
2711
+** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
27082712
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
27092713
** the legacy [double-quoted string literal] misfeature for DML statements
27102714
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
27112715
** default value of this setting is determined by the [-DSQLITE_DQS]
27122716
** compile-time option.
27132717
** </dd>
27142718
**
27152719
** [[SQLITE_DBCONFIG_DQS_DDL]]
2716
-** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
2720
+** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
27172721
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
27182722
** the legacy [double-quoted string literal] misfeature for DDL statements,
27192723
** such as CREATE TABLE and CREATE INDEX. The
27202724
** default value of this setting is determined by the [-DSQLITE_DQS]
27212725
** compile-time option.
27222726
** </dd>
27232727
**
27242728
** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
2725
-** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td>
2729
+** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
27262730
** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
27272731
** assume that database schemas are untainted by malicious content.
27282732
** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
27292733
** takes additional defensive steps to protect the application from harm
27302734
** including:
@@ -2740,20 +2744,20 @@
27402744
** all applications are advised to turn it off if possible. This setting
27412745
** can also be controlled using the [PRAGMA trusted_schema] statement.
27422746
** </dd>
27432747
**
27442748
** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
2745
-** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td>
2749
+** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
27462750
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
27472751
** the legacy file format flag. When activated, this flag causes all newly
27482752
** created database file to have a schema format version number (the 4-byte
27492753
** integer found at offset 44 into the database header) of 1. This in turn
27502754
** means that the resulting database file will be readable and writable by
27512755
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
27522756
** newly created databases are generally not understandable by SQLite versions
27532757
** 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
27552759
** all the way back to version 3.0.0, and so this setting is of little
27562760
** practical use, but is provided so that SQLite can continue to claim the
27572761
** ability to generate new database files that are compatible with version
27582762
** 3.0.0.
27592763
** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on,
@@ -2762,27 +2766,39 @@
27622766
** not considered a bug since SQLite versions 3.3.0 and earlier do not support
27632767
** either generated columns or decending indexes.
27642768
** </dd>
27652769
**
27662770
** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
2767
-** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td>
2771
+** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
27682772
** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
27692773
** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
27702774
** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
27712775
** statistics. For statistics to be collected, the flag must be set on
27722776
** the database handle both when the SQL statement is prepared and when it
27732777
** 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>
27752785
**
27762786
** [[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
27792789
** in which tables and indexes are scanned so that the scans start at the end
27802790
** 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>
27842800
**
27852801
** </dl>
27862802
*/
27872803
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
27882804
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@@ -2800,11 +2816,11 @@
28002816
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
28012817
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
28022818
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
28032819
#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
28042820
#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* */
28062822
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
28072823
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
28082824
28092825
/*
28102826
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -6547,10 +6563,17 @@
65476563
** ^SQLite implements this interface by calling the xSleep()
65486564
** method of the default [sqlite3_vfs] object. If the xSleep() method
65496565
** of the default VFS is not implemented correctly, or not implemented at
65506566
** all, then the behavior of sqlite3_sleep() may deviate from the description
65516567
** 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.
65526575
*/
65536576
SQLITE_API int sqlite3_sleep(int);
65546577
65556578
/*
65566579
** CAPI3REF: Name Of The Folder Holding Temporary Files
@@ -8174,13 +8197,13 @@
81748197
** ^The sqlite3_mutex_leave() routine exits a mutex that was
81758198
** previously entered by the same thread. The behavior
81768199
** is undefined if the mutex is not currently entered by the
81778200
** calling thread or is not currently allocated.
81788201
**
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.
81828205
**
81838206
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
81848207
*/
81858208
SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int);
81868209
SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*);
@@ -11106,20 +11129,24 @@
1110611129
** [sqlite3session_create()] for details.
1110711130
*/
1110811131
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
1110911132
1111011133
/*
11111
-** CAPIREF: Conigure a Session Object
11134
+** CAPI3REF: Configure a Session Object
1111211135
** METHOD: sqlite3_session
1111311136
**
1111411137
** 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].
1111711140
**
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
1111911146
**
11120
-** The following values may passed as the the 4th parameter to
11147
+** The following values may passed as the the 2nd parameter to
1112111148
** sqlite3session_object_config().
1112211149
**
1112311150
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
1112411151
** This option is used to set, clear or query the flag that enables
1112511152
** the [sqlite3session_changeset_size()] API. Because it imposes some
@@ -11131,16 +11158,25 @@
1113111158
** variable is set to 1 if the sqlite3session_changeset_size() API is
1113211159
** enabled following the current call, or 0 otherwise.
1113311160
**
1113411161
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
1113511162
** 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.
1113611175
*/
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
1114211178
1114311179
/*
1114411180
** CAPI3REF: Enable Or Disable A Session Object
1114511181
** METHOD: sqlite3_session
1114611182
**
@@ -13652,11 +13688,11 @@
1365213688
# define SQLITE_INLINE __forceinline
1365313689
#else
1365413690
# define SQLITE_NOINLINE
1365513691
# define SQLITE_INLINE
1365613692
#endif
13657
-#if defined(SQLITE_COVERAGE_TEST)
13693
+#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__)
1365813694
# undef SQLITE_INLINE
1365913695
# define SQLITE_INLINE
1366013696
#endif
1366113697
1366213698
/*
@@ -18264,10 +18300,11 @@
1826418300
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
1826518301
unsigned bHasExpr:1; /* Index contains an expression, either a literal
1826618302
** expression, or a reference to a VIRTUAL column */
1826718303
#ifdef SQLITE_ENABLE_STAT4
1826818304
int nSample; /* Number of elements in aSample[] */
18305
+ int mxSample; /* Number of slots allocated to aSample[] */
1826918306
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
1827018307
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
1827118308
IndexSample *aSample; /* Samples of the left-most key */
1827218309
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
1827318310
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
@@ -20020,19 +20057,23 @@
2002020057
# define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02)
2002120058
# define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
2002220059
# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
2002320060
# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
2002420061
# 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)
2002520064
#else
2002620065
# define sqlite3Toupper(x) toupper((unsigned char)(x))
2002720066
# define sqlite3Isspace(x) isspace((unsigned char)(x))
2002820067
# define sqlite3Isalnum(x) isalnum((unsigned char)(x))
2002920068
# define sqlite3Isalpha(x) isalpha((unsigned char)(x))
2003020069
# define sqlite3Isdigit(x) isdigit((unsigned char)(x))
2003120070
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
2003220071
# define sqlite3Tolower(x) tolower((unsigned char)(x))
2003320072
# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
20073
+# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0')
20074
+# define sqlite3JsonId2(x) sqlite3IsIdChar(x)
2003420075
#endif
2003520076
SQLITE_PRIVATE int sqlite3IsIdChar(u8);
2003620077
2003720078
/*
2003820079
** Internal function prototypes
@@ -20465,11 +20506,11 @@
2046520506
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
2046620507
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
2046720508
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
2046820509
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
2046920510
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
20470
-SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*);
20511
+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
2047120512
#ifdef SQLITE_ENABLE_CURSOR_HINTS
2047220513
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
2047320514
#endif
2047420515
SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*);
2047520516
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
@@ -22161,11 +22202,11 @@
2216122202
** isalpha() 0x02
2216222203
** isdigit() 0x04
2216322204
** isalnum() 0x06
2216422205
** isxdigit() 0x08
2216522206
** toupper() 0x20
22166
-** SQLite identifier character 0x40
22207
+** SQLite identifier character 0x40 $, _, or non-ascii
2216722208
** Quote character 0x80
2216822209
**
2216922210
** Bit 0x20 is set if the mapped character requires translation to upper
2217022211
** case. i.e. if the character is a lower-case ASCII character.
2217122212
** If x is a lower-case ASCII character, then its upper-case equivalent
@@ -23654,10 +23695,11 @@
2365423695
char validYMD; /* True (1) if Y,M,D are valid */
2365523696
char validHMS; /* True (1) if h,m,s are valid */
2365623697
char validTZ; /* True (1) if tz is valid */
2365723698
char tzSet; /* Timezone was set explicitly */
2365823699
char isError; /* An overflow has occurred */
23700
+ char useSubsec; /* Display subsecond precision */
2365923701
};
2366023702
2366123703
2366223704
/*
2366323705
** Convert zDate into one or more integers according to the conversion
@@ -23968,10 +24010,15 @@
2396824010
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
2396924011
return setDateTimeToCurrent(context, p);
2397024012
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
2397124013
setRawDateNumber(p, r);
2397224014
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);
2397324020
}
2397424021
return 1;
2397524022
}
2397624023
2397724024
/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999.
@@ -24382,12 +24429,26 @@
2438224429
/*
2438324430
** start of TTTTT
2438424431
**
2438524432
** Move the date backwards to the beginning of the current day,
2438624433
** 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').
2438724440
*/
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
+ }
2438924450
if( !p->validJD && !p->validYMD && !p->validHMS ) break;
2439024451
z += 9;
2439124452
computeYMD(p);
2439224453
p->validHMS = 1;
2439324454
p->h = p->m = 0;
@@ -24581,11 +24642,15 @@
2458124642
sqlite3_value **argv
2458224643
){
2458324644
DateTime x;
2458424645
if( isDate(context, argc, argv, &x)==0 ){
2458524646
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
+ }
2458724652
}
2458824653
}
2458924654
2459024655
/*
2459124656
** datetime( TIMESTRING, MOD, MOD, ...)
@@ -24597,12 +24662,12 @@
2459724662
int argc,
2459824663
sqlite3_value **argv
2459924664
){
2460024665
DateTime x;
2460124666
if( isDate(context, argc, argv, &x)==0 ){
24602
- int Y, s;
24603
- char zBuf[24];
24667
+ int Y, s, n;
24668
+ char zBuf[32];
2460424669
computeYMD_HMS(&x);
2460524670
Y = x.Y;
2460624671
if( Y<0 ) Y = -Y;
2460724672
zBuf[1] = '0' + (Y/1000)%10;
2460824673
zBuf[2] = '0' + (Y/100)%10;
@@ -24619,19 +24684,32 @@
2461924684
zBuf[13] = '0' + (x.h)%10;
2462024685
zBuf[14] = ':';
2462124686
zBuf[15] = '0' + (x.m/10)%10;
2462224687
zBuf[16] = '0' + (x.m)%10;
2462324688
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
+ }
2462824706
if( x.Y<0 ){
2462924707
zBuf[0] = '-';
24630
- sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT);
24708
+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
2463124709
}else{
24632
- sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT);
24710
+ sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT);
2463324711
}
2463424712
}
2463524713
}
2463624714
2463724715
/*
@@ -24644,24 +24722,37 @@
2464424722
int argc,
2464524723
sqlite3_value **argv
2464624724
){
2464724725
DateTime x;
2464824726
if( isDate(context, argc, argv, &x)==0 ){
24649
- int s;
24727
+ int s, n;
2465024728
char zBuf[16];
2465124729
computeHMS(&x);
2465224730
zBuf[0] = '0' + (x.h/10)%10;
2465324731
zBuf[1] = '0' + (x.h)%10;
2465424732
zBuf[2] = ':';
2465524733
zBuf[3] = '0' + (x.m/10)%10;
2465624734
zBuf[4] = '0' + (x.m)%10;
2465724735
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);
2466324754
}
2466424755
}
2466524756
2466624757
/*
2466724758
** date( TIMESTRING, MOD, MOD, ...)
@@ -24788,12 +24879,17 @@
2478824879
case 'M': {
2478924880
sqlite3_str_appendf(&sRes,"%02d",x.m);
2479024881
break;
2479124882
}
2479224883
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
+ }
2479524891
break;
2479624892
}
2479724893
case 'S': {
2479824894
sqlite3_str_appendf(&sRes,"%02d",(int)x.s);
2479924895
break;
@@ -30667,10 +30763,11 @@
3066730763
nsd = 16 + flag_altform2*10;
3066830764
bufpt = buf;
3066930765
{
3067030766
i64 szBufNeeded; /* Size of a temporary buffer needed */
3067130767
szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
30768
+ if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3;
3067230769
if( szBufNeeded > etBUFSIZE ){
3067330770
bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
3067430771
if( bufpt==0 ) return;
3067530772
}
3067630773
}
@@ -30684,14 +30781,16 @@
3068430781
if( e2<0 ){
3068530782
*(bufpt++) = '0';
3068630783
}else if( msd>0 ){
3068730784
for(; e2>=0; e2--){
3068830785
*(bufpt++) = et_getdigit_int(&longvalue,&msd);
30786
+ if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
3068930787
}
3069030788
}else{
3069130789
for(; e2>=0; e2--){
3069230790
*(bufpt++) = et_getdigit(&realvalue,&nsd);
30791
+ if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
3069330792
}
3069430793
}
3069530794
/* The decimal point */
3069630795
if( flag_dp ){
3069730796
*(bufpt++) = '.';
@@ -34434,17 +34533,19 @@
3443434533
}else{
3443534534
x = v;
3443634535
}
3443734536
i = sizeof(zTemp)-2;
3443834537
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';
3444134540
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;
3444634547
}
3444734548
3444834549
/*
3444934550
** Compare the 19-character string zNum against the text representation
3445034551
** value 2^63: 9223372036854775808. Return negative, zero, or positive
@@ -34605,11 +34706,13 @@
3460534706
for(i=2; z[i]=='0'; i++){}
3460634707
for(k=i; sqlite3Isxdigit(z[k]); k++){
3460734708
u = u*16 + sqlite3HexToInt(z[k]);
3460834709
}
3460934710
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;
3461134714
}else
3461234715
#endif /* SQLITE_OMIT_HEX_INTEGER */
3461334716
{
3461434717
return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
3461534718
}
@@ -34641,11 +34744,11 @@
3464134744
&& sqlite3Isxdigit(zNum[2])
3464234745
){
3464334746
u32 u = 0;
3464434747
zNum += 2;
3464534748
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++){
3464734750
u = u*16 + sqlite3HexToInt(zNum[i]);
3464834751
}
3464934752
if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
3465034753
memcpy(pValue, &u, 4);
3465134754
return 1;
@@ -37137,11 +37240,11 @@
3713737240
# define SQLITE_ENABLE_LOCKING_STYLE 0
3713837241
# endif
3713937242
#endif
3714037243
3714137244
/* Use pread() and pwrite() if they are available */
37142
-#if defined(__APPLE__)
37245
+#if defined(__APPLE__) || defined(__linux__)
3714337246
# define HAVE_PREAD 1
3714437247
# define HAVE_PWRITE 1
3714537248
#endif
3714637249
#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
3714737250
# undef USE_PREAD
@@ -40387,16 +40490,10 @@
4038740490
4038840491
/*
4038940492
** Seek to the offset passed as the second argument, then read cnt
4039040493
** bytes into pBuf. Return the number of bytes actually read.
4039140494
**
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
-**
4039840495
** To avoid stomping the errno value on a failed read the lastErrno value
4039940496
** is set before returning.
4040040497
*/
4040140498
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
4040240499
int got;
@@ -50419,11 +50516,11 @@
5041950516
&extendedParameters);
5042050517
if( h!=INVALID_HANDLE_VALUE ) break;
5042150518
if( isReadWrite ){
5042250519
int rc2, isRO = 0;
5042350520
sqlite3BeginBenignMalloc();
50424
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
50521
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
5042550522
sqlite3EndBenignMalloc();
5042650523
if( rc2==SQLITE_OK && isRO ) break;
5042750524
}
5042850525
}while( winRetryIoerr(&cnt, &lastErrno) );
5042950526
#else
@@ -50436,11 +50533,11 @@
5043650533
NULL);
5043750534
if( h!=INVALID_HANDLE_VALUE ) break;
5043850535
if( isReadWrite ){
5043950536
int rc2, isRO = 0;
5044050537
sqlite3BeginBenignMalloc();
50441
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
50538
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
5044250539
sqlite3EndBenignMalloc();
5044350540
if( rc2==SQLITE_OK && isRO ) break;
5044450541
}
5044550542
}while( winRetryIoerr(&cnt, &lastErrno) );
5044650543
#endif
@@ -50456,11 +50553,11 @@
5045650553
NULL);
5045750554
if( h!=INVALID_HANDLE_VALUE ) break;
5045850555
if( isReadWrite ){
5045950556
int rc2, isRO = 0;
5046050557
sqlite3BeginBenignMalloc();
50461
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
50558
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
5046250559
sqlite3EndBenignMalloc();
5046350560
if( rc2==SQLITE_OK && isRO ) break;
5046450561
}
5046550562
}while( winRetryIoerr(&cnt, &lastErrno) );
5046650563
}
@@ -50678,10 +50775,17 @@
5067850775
UNUSED_PARAMETER(pVfs);
5067950776
5068050777
SimulateIOError( return SQLITE_IOERR_ACCESS; );
5068150778
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
5068250779
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
+ }
5068350787
5068450788
zConverted = winConvertFromUtf8Filename(zFilename);
5068550789
if( zConverted==0 ){
5068650790
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
5068750791
return SQLITE_IOERR_NOMEM_BKPT;
@@ -52835,15 +52939,19 @@
5283552939
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
5283652940
static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){
5283752941
PgHdr *pPg;
5283852942
unsigned char *a;
5283952943
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
+ }
5284552953
}
5284652954
static void pcacheDump(PCache *pCache){
5284752955
int N;
5284852956
int i;
5284952957
sqlite3_pcache_page *pLower;
@@ -52852,13 +52960,12 @@
5285252960
if( pCache->pCache==0 ) return;
5285352961
N = sqlite3PcachePagecount(pCache);
5285452962
if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
5285552963
for(i=1; i<=N; i++){
5285652964
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
52857
- if( pLower==0 ) continue;
5285852965
pcachePageTrace(i, pLower);
52859
- if( ((PgHdr*)pLower)->pPage==0 ){
52966
+ if( pLower && ((PgHdr*)pLower)->pPage==0 ){
5286052967
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
5286152968
}
5286252969
}
5286352970
}
5286452971
#else
@@ -58242,10 +58349,12 @@
5824258349
*/
5824358350
static int pager_truncate(Pager *pPager, Pgno nPage){
5824458351
int rc = SQLITE_OK;
5824558352
assert( pPager->eState!=PAGER_ERROR );
5824658353
assert( pPager->eState!=PAGER_READER );
58354
+ PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage));
58355
+
5824758356
5824858357
if( isOpen(pPager->fd)
5824958358
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
5825058359
){
5825158360
i64 currentSize, newSize;
@@ -61159,10 +61268,14 @@
6115961268
6116061269
assert( !isOpen(pPager->fd) || !MEMDB );
6116161270
if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
6116261271
if( pgno>pPager->mxPgno ){
6116361272
rc = SQLITE_FULL;
61273
+ if( pgno<=pPager->dbSize ){
61274
+ sqlite3PcacheRelease(pPg);
61275
+ pPg = 0;
61276
+ }
6116461277
goto pager_acquire_err;
6116561278
}
6116661279
if( noContent ){
6116761280
/* Failure to set the bits in the InJournal bit-vectors is benign.
6116861281
** It merely means that we might do some extra work to journal a
@@ -61323,14 +61436,16 @@
6132361436
}
6132461437
6132561438
/*
6132661439
** Release a page reference.
6132761440
**
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.
6133061443
** 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.
6133261447
**
6133361448
** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine
6133461449
** checks the total number of outstanding pages and if the number of
6133561450
** pages reaches zero it drops the database lock.
6133661451
*/
@@ -61342,11 +61457,11 @@
6134261457
pagerReleaseMapPage(pPg);
6134361458
}else{
6134461459
sqlite3PcacheRelease(pPg);
6134561460
}
6134661461
/* 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 */
6134861463
}
6134961464
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
6135061465
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
6135161466
}
6135261467
SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){
@@ -63102,17 +63217,19 @@
6310263217
** Attempt to take an exclusive lock on the database file. If a PENDING lock
6310363218
** is obtained instead, immediately release it.
6310463219
*/
6310563220
static int pagerExclusiveLock(Pager *pPager){
6310663221
int rc; /* Return code */
63222
+ u8 eOrigLock; /* Original lock */
6310763223
63108
- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
63224
+ assert( pPager->eLock>=SHARED_LOCK );
63225
+ eOrigLock = pPager->eLock;
6310963226
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
6311063227
if( rc!=SQLITE_OK ){
6311163228
/* If the attempt to grab the exclusive lock failed, release the
6311263229
** pending lock that may have been obtained instead. */
63113
- pagerUnlockDb(pPager, SHARED_LOCK);
63230
+ pagerUnlockDb(pPager, eOrigLock);
6311463231
}
6311563232
6311663233
return rc;
6311763234
}
6311863235
@@ -64113,23 +64230,44 @@
6411364230
}
6411464231
6411564232
assert( nByte>=8 );
6411664233
assert( (nByte&0x00000007)==0 );
6411764234
assert( nByte<=65536 );
64235
+ assert( nByte%4==0 );
6411864236
64119
- if( nativeCksum ){
64120
- do {
64121
- s1 += *aData++ + s2;
64122
- s2 += *aData++ + s1;
64123
- }while( aData<aEnd );
64124
- }else{
64237
+ if( !nativeCksum ){
6412564238
do {
6412664239
s1 += BYTESWAP32(aData[0]) + s2;
6412764240
s2 += BYTESWAP32(aData[1]) + s1;
6412864241
aData += 2;
6412964242
}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 );
6413064267
}
64268
+ assert( aData==aEnd );
6413164269
6413264270
aOut[0] = s1;
6413364271
aOut[1] = s2;
6413464272
}
6413564273
@@ -67056,11 +67194,13 @@
6705667194
if( pWal->syncHeader ){
6705767195
rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
6705867196
if( rc ) return rc;
6705967197
}
6706067198
}
67061
- assert( (int)pWal->szPage==szPage );
67199
+ if( (int)pWal->szPage!=szPage ){
67200
+ return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */
67201
+ }
6706267202
6706367203
/* Setup information needed to write frames into the WAL */
6706467204
w.pWal = pWal;
6706567205
w.pFd = pWal->pWalFd;
6706667206
w.iSyncPoint = 0;
@@ -67716,11 +67856,11 @@
6771667856
** Cell content makes use of variable length integers. A variable
6771767857
** length integer is 1 to 9 bytes where the lower 7 bits of each
6771867858
** byte are used. The integer consists of all bytes that have bit 8 set and
6771967859
** the first byte with bit 8 clear. The most significant byte of the integer
6772067860
** 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
6772267862
** allows a 64-bit integer to be encoded in 9 bytes.
6772367863
**
6772467864
** 0x00 becomes 0x00000000
6772567865
** 0x7f becomes 0x0000007f
6772667866
** 0x81 0x00 becomes 0x00000080
@@ -68100,11 +68240,11 @@
6810068240
** Legal values for BtCursor.curFlags
6810168241
*/
6810268242
#define BTCF_WriteFlag 0x01 /* True if a write cursor */
6810368243
#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
6810468244
#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 */
6810668246
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
6810768247
#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */
6810868248
#define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */
6810968249
6811068250
/*
@@ -76202,11 +76342,11 @@
7620276342
aAfter[j] = iAfter;
7620376343
break;
7620476344
}
7620576345
}
7620676346
if( j>=nFree ){
76207
- if( nFree>=sizeof(aOfst)/sizeof(aOfst[0]) ){
76347
+ if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){
7620876348
for(j=0; j<nFree; j++){
7620976349
freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]);
7621076350
}
7621176351
nFree = 0;
7621276352
}
@@ -76270,11 +76410,11 @@
7627076410
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
7627176411
assert( nCell>=nTail );
7627276412
nCell -= nTail;
7627376413
}
7627476414
76275
- pData = &aData[get2byteNotZero(&aData[hdr+5])];
76415
+ pData = &aData[get2byte(&aData[hdr+5])];
7627676416
if( pData<pBegin ) goto editpage_fail;
7627776417
if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail;
7627876418
7627976419
/* Add cells to the start of the page */
7628076420
if( iNew<iOld ){
@@ -77907,11 +78047,11 @@
7790778047
return btreeOverwriteCell(pCur, &x2);
7790878048
}
7790978049
}
7791078050
}
7791178051
assert( pCur->eState==CURSOR_VALID
77912
- || (pCur->eState==CURSOR_INVALID && loc) );
78052
+ || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB );
7791378053
7791478054
pPage = pCur->pPage;
7791578055
assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
7791678056
assert( pPage->leaf || !pPage->intKey );
7791778057
if( pPage->nFree<0 ){
@@ -80157,17 +80297,11 @@
8015780297
assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
8015880298
assert( p->bDestLocked );
8015980299
assert( !isFatalError(p->rc) );
8016080300
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
8016180301
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 );
8016980303
8017080304
/* This loop runs once for each destination page spanned by the source
8017180305
** page. For each iteration, variable iOff is set to the byte offset
8017280306
** of the destination page.
8017380307
*/
@@ -80296,11 +80430,14 @@
8029680430
/* Do not allow backup if the destination database is in WAL mode
8029780431
** and the page sizes are different between source and destination */
8029880432
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
8029980433
pgszDest = sqlite3BtreeGetPageSize(p->pDest);
8030080434
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
+ ){
8030280439
rc = SQLITE_READONLY;
8030380440
}
8030480441
8030580442
/* Now that there is a read-lock on the source database, query the
8030680443
** source pager for the number of pages in the database.
@@ -80845,10 +80982,11 @@
8084580982
Mem tmp;
8084680983
char zBuf[100];
8084780984
char *z;
8084880985
int i, j, incr;
8084980986
if( (p->flags & MEM_Str)==0 ) return 1;
80987
+ if( p->db && p->db->mallocFailed ) return 1;
8085080988
if( p->flags & MEM_Term ){
8085180989
/* Insure that the string is properly zero-terminated. Pay particular
8085280990
** attention to the case where p->n is odd */
8085380991
if( p->szMalloc>0 && p->z==p->zMalloc ){
8085480992
assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 );
@@ -83508,10 +83646,12 @@
8350883646
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
8350983647
int nMaxArgs = *pMaxFuncArgs;
8351083648
Op *pOp;
8351183649
Parse *pParse = p->pParse;
8351283650
int *aLabel = pParse->aLabel;
83651
+
83652
+ assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */
8351383653
p->readOnly = 1;
8351483654
p->bIsReader = 0;
8351583655
pOp = &p->aOp[p->nOp-1];
8351683656
assert( p->aOp[0].opcode==OP_Init );
8351783657
while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){
@@ -83567,10 +83707,11 @@
8356783707
/* The mkopcodeh.tcl script has so arranged things that the only
8356883708
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
8356983709
** have non-negative values for P2. */
8357083710
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
8357183711
assert( ADDR(pOp->p2)<-pParse->nLabel );
83712
+ assert( aLabel!=0 ); /* True because of tag-20230419-1 */
8357283713
pOp->p2 = aLabel[ADDR(pOp->p2)];
8357383714
}
8357483715
break;
8357583716
}
8357683717
}
@@ -84310,11 +84451,11 @@
8431084451
}
8431184452
}
8431284453
8431384454
/* Return the most recently added opcode
8431484455
*/
84315
-VdbeOp * sqlite3VdbeGetLastOp(Vdbe *p){
84456
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){
8431684457
return sqlite3VdbeGetOp(p, p->nOp - 1);
8431784458
}
8431884459
8431984460
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
8432084461
/*
@@ -88039,10 +88180,20 @@
8803988180
sqlite3 *db = v->db;
8804088181
i64 iKey2;
8804188182
PreUpdate preupdate;
8804288183
const char *zTbl = pTab->zName;
8804388184
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
8804488195
8804588196
assert( db->pPreUpdate==0 );
8804688197
memset(&preupdate, 0, sizeof(PreUpdate));
8804788198
if( HasRowid(pTab)==0 ){
8804888199
iKey1 = iKey2 = 0;
@@ -88055,12 +88206,12 @@
8805588206
}
8805688207
}
8805788208
8805888209
assert( pCsr!=0 );
8805988210
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)
8806288213
);
8806388214
8806488215
preupdate.v = v;
8806588216
preupdate.pCsr = pCsr;
8806688217
preupdate.op = op;
@@ -89429,13 +89580,13 @@
8942989580
p = (Vdbe *)pStmt;
8943089581
db = p->db;
8943189582
assert( db!=0 );
8943289583
n = sqlite3_column_count(pStmt);
8943389584
if( N<n && N>=0 ){
89585
+ u8 prior_mallocFailed = db->mallocFailed;
8943489586
N += useType*n;
8943589587
sqlite3_mutex_enter(db->mutex);
89436
- assert( db->mallocFailed==0 );
8943789588
#ifndef SQLITE_OMIT_UTF16
8943889589
if( useUtf16 ){
8943989590
ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]);
8944089591
}else
8944189592
#endif
@@ -89443,11 +89594,12 @@
8944389594
ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]);
8944489595
}
8944589596
/* A malloc may have failed inside of the _text() call. If this
8944689597
** is the case, clear the mallocFailed flag and return NULL.
8944789598
*/
89448
- if( db->mallocFailed ){
89599
+ assert( db->mallocFailed==0 || db->mallocFailed==1 );
89600
+ if( db->mallocFailed > prior_mallocFailed ){
8944989601
sqlite3OomClear(db);
8945089602
ret = 0;
8945189603
}
8945289604
sqlite3_mutex_leave(db->mutex);
8945389605
}
@@ -92987,11 +93139,11 @@
9298793139
}
9298893140
9298993141
/* Opcode: Jump P1 P2 P3 * *
9299093142
**
9299193143
** 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,
9299393145
** equal to, or greater than the P2 vector, respectively.
9299493146
**
9299593147
** This opcode must immediately follow an OP_Compare opcode.
9299693148
*/
9299793149
case OP_Jump: { /* jump */
@@ -93333,11 +93485,11 @@
9333393485
*/
9333493486
case OP_IfNullRow: { /* jump */
9333593487
VdbeCursor *pC;
9333693488
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
9333793489
pC = p->apCsr[pOp->p1];
93338
- if( ALWAYS(pC) && pC->nullRow ){
93490
+ if( pC && pC->nullRow ){
9333993491
sqlite3VdbeMemSetNull(aMem + pOp->p3);
9334093492
goto jump_to_p2;
9334193493
}
9334293494
break;
9334393495
}
@@ -93828,11 +93980,11 @@
9382893980
pIn1->flags |= MEM_IntReal;
9382993981
pIn1->flags &= ~MEM_Int;
9383093982
}else{
9383193983
pIn1->u.r = (double)pIn1->u.i;
9383293984
pIn1->flags |= MEM_Real;
93833
- pIn1->flags &= ~MEM_Int;
93985
+ pIn1->flags &= ~(MEM_Int|MEM_Str);
9383493986
}
9383593987
}
9383693988
REGISTER_TRACE((int)(pIn1-aMem), pIn1);
9383793989
zAffinity++;
9383893990
if( zAffinity[0]==0 ) break;
@@ -99841,11 +99993,11 @@
9984199993
*ppBlob = (sqlite3_blob *)pBlob;
9984299994
}else{
9984399995
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
9984499996
sqlite3DbFree(db, pBlob);
9984599997
}
99846
- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
99998
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
9984799999
sqlite3DbFree(db, zErr);
99848100000
sqlite3ParseObjectReset(&sParse);
99849100001
rc = sqlite3ApiExit(db, rc);
99850100002
sqlite3_mutex_leave(db->mutex);
99851100003
return rc;
@@ -100000,11 +100152,11 @@
100000100152
}else{
100001100153
char *zErr;
100002100154
((Vdbe*)p->pStmt)->rc = SQLITE_OK;
100003100155
rc = blobSeekToRow(p, iRow, &zErr);
100004100156
if( rc!=SQLITE_OK ){
100005
- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
100157
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
100006100158
sqlite3DbFree(db, zErr);
100007100159
}
100008100160
assert( rc!=SQLITE_SCHEMA );
100009100161
}
100010100162
@@ -107198,28 +107350,31 @@
107198107350
107199107351
/*
107200107352
** Join two expressions using an AND operator. If either expression is
107201107353
** NULL, then just return the other expression.
107202107354
**
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.
107206107358
*/
107207107359
SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){
107208107360
sqlite3 *db = pParse->db;
107209107361
if( pLeft==0 ){
107210107362
return pRight;
107211107363
}else if( pRight==0 ){
107212107364
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");
107219107365
}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
+ }
107221107376
}
107222107377
}
107223107378
107224107379
/*
107225107380
** Construct a new expression node for a function with multiple
@@ -108460,16 +108615,21 @@
108460108615
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
108461108616
return exprIsConst(p, 3, iCur);
108462108617
}
108463108618
108464108619
/*
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
+**
108466108626
** This is an optimization. False negatives will perhaps cause slower
108467108627
** queries, but false positives will yield incorrect answers. So when in
108468108628
** doubt, return 0.
108469108629
**
108470
-** To be an invariant constraint, the following must be true:
108630
+** To be an single-source constraint, the following must be true:
108471108631
**
108472108632
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
108473108633
**
108474108634
** (2) pExpr cannot use subqueries or non-deterministic functions.
108475108635
**
@@ -108476,25 +108636,56 @@
108476108636
** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
108477108637
** (Is there some way to relax this constraint?)
108478108638
**
108479108639
** (4) If pSrc is the right operand of a LEFT JOIN, then...
108480108640
** (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.
108482108642
**
108483108643
** (5) If pSrc is not the right operand of a LEFT JOIN or the left
108484108644
** operand of a RIGHT JOIN, then pExpr must be from the WHERE
108485108645
** 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.
108486108659
*/
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];
108488108666
if( pSrc->fg.jointype & JT_LTORJ ){
108489108667
return 0; /* rule (3) */
108490108668
}
108491108669
if( pSrc->fg.jointype & JT_LEFT ){
108492108670
if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */
108493108671
if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */
108494108672
}else{
108495108673
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
+ }
108496108687
}
108497108688
return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
108498108689
}
108499108690
108500108691
@@ -112373,11 +112564,11 @@
112373112564
int iAgg = pExpr->iAgg;
112374112565
Parse *pParse = pWalker->pParse;
112375112566
sqlite3 *db = pParse->db;
112376112567
assert( iAgg>=0 );
112377112568
if( pExpr->op!=TK_AGG_FUNCTION ){
112378
- if( ALWAYS(iAgg<pAggInfo->nColumn)
112569
+ if( iAgg<pAggInfo->nColumn
112379112570
&& pAggInfo->aCol[iAgg].pCExpr==pExpr
112380112571
){
112381112572
pExpr = sqlite3ExprDup(db, pExpr, 0);
112382112573
if( pExpr ){
112383112574
pAggInfo->aCol[iAgg].pCExpr = pExpr;
@@ -112578,11 +112769,11 @@
112578112769
findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
112579112770
break;
112580112771
} /* endif pExpr->iTable==pItem->iCursor */
112581112772
} /* end loop over pSrcList */
112582112773
}
112583
- return WRC_Prune;
112774
+ return WRC_Continue;
112584112775
}
112585112776
case TK_AGG_FUNCTION: {
112586112777
if( (pNC->ncFlags & NC_InAggFunc)==0
112587112778
&& pWalker->walkerDepth==pExpr->op2
112588112779
){
@@ -114075,10 +114266,23 @@
114075114266
}
114076114267
114077114268
sqlite3_free(zQuot);
114078114269
return rc;
114079114270
}
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
+}
114080114284
114081114285
/*
114082114286
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
114083114287
** it was read from the schema of database zDb. Return SQLITE_OK if
114084114288
** successful. Otherwise, return an SQLite error code and leave an error
@@ -114123,11 +114327,21 @@
114123114327
if( pSel==0 ){
114124114328
pStep->pExprList = 0;
114125114329
pSrc = 0;
114126114330
rc = SQLITE_NOMEM;
114127114331
}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);
114128114341
sqlite3SelectPrep(pParse, pSel, 0);
114342
+ renameSetENames(pStep->pExprList, ENAME_NAME);
114129114343
rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
114130114344
assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
114131114345
assert( pSrc==pSel->pSrc );
114132114346
if( pStep->pExprList ) pSel->pEList = 0;
114133114347
pSel->pSrc = 0;
@@ -116859,17 +117073,22 @@
116859117073
if( zIndex==0 ) continue;
116860117074
nSample = sqlite3_column_int(pStmt, 1);
116861117075
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
116862117076
assert( pIdx==0 || pIdx->nSample==0 );
116863117077
if( pIdx==0 ) continue;
117078
+ if( pIdx->aSample!=0 ){
117079
+ /* The same index appears in sqlite_stat4 under multiple names */
117080
+ continue;
117081
+ }
116864117082
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
116865117083
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
116866117084
nIdxCol = pIdx->nKeyCol;
116867117085
}else{
116868117086
nIdxCol = pIdx->nColumn;
116869117087
}
116870117088
pIdx->nSampleCol = nIdxCol;
117089
+ pIdx->mxSample = nSample;
116871117090
nByte = sizeof(IndexSample) * nSample;
116872117091
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
116873117092
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
116874117093
116875117094
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
@@ -116905,10 +117124,15 @@
116905117124
116906117125
zIndex = (char *)sqlite3_column_text(pStmt, 0);
116907117126
if( zIndex==0 ) continue;
116908117127
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
116909117128
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
+ }
116910117134
/* This next condition is true if data has already been loaded from
116911117135
** the sqlite_stat4 table. */
116912117136
nCol = pIdx->nSampleCol;
116913117137
if( pIdx!=pPrevIdx ){
116914117138
initAvgEq(pPrevIdx);
@@ -116948,15 +117172,16 @@
116948117172
static int loadStat4(sqlite3 *db, const char *zDb){
116949117173
int rc = SQLITE_OK; /* Result codes from subroutines */
116950117174
const Table *pStat4;
116951117175
116952117176
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
116954117179
&& IsOrdinaryTable(pStat4)
116955117180
){
116956117181
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",
116958117183
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
116959117184
zDb
116960117185
);
116961117186
}
116962117187
return rc;
@@ -118796,11 +119021,11 @@
118796119021
}
118797119022
118798119023
if( IsOrdinaryTable(pTable) ){
118799119024
sqlite3FkDelete(db, pTable);
118800119025
}
118801
-#ifndef SQLITE_OMIT_VIRTUAL_TABLE
119026
+#ifndef SQLITE_OMIT_VIRTUALTABLE
118802119027
else if( IsVirtual(pTable) ){
118803119028
sqlite3VtabClear(db, pTable);
118804119029
}
118805119030
#endif
118806119031
else{
@@ -123828,10 +124053,11 @@
123828124053
db->enc = enc;
123829124054
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
123830124055
** strings is BINARY.
123831124056
*/
123832124057
db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
124058
+ sqlite3ExpirePreparedStatements(db, 1);
123833124059
}
123834124060
123835124061
/*
123836124062
** This function is responsible for invoking the collation factory callback
123837124063
** or substituting a collation sequence of a different encoding when the
@@ -126724,11 +126950,11 @@
126724126950
126725126951
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
126726126952
/*
126727126953
** The "unknown" function is automatically substituted in place of
126728126954
** 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.
126730126956
** When the "sqlite3" command-line shell is built using this functionality,
126731126957
** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries
126732126958
** involving application-defined functions to be examined in a generic
126733126959
** sqlite3 shell.
126734126960
*/
@@ -129027,26 +129253,26 @@
129027129253
zFrom = pFKey->pFrom->zName;
129028129254
nFrom = sqlite3Strlen30(zFrom);
129029129255
129030129256
if( action==OE_Restrict ){
129031129257
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
129032
- Token tFrom;
129033
- Token tDb;
129258
+ SrcList *pSrc;
129034129259
Expr *pRaise;
129035129260
129036
- tFrom.z = zFrom;
129037
- tFrom.n = nFrom;
129038
- tDb.z = db->aDb[iDb].zDbSName;
129039
- tDb.n = sqlite3Strlen30(tDb.z);
129040
-
129041129261
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
129042129262
if( pRaise ){
129043129263
pRaise->affExpr = OE_Abort;
129044129264
}
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
+ }
129045129271
pSelect = sqlite3SelectNew(pParse,
129046129272
sqlite3ExprListAppend(pParse, 0, pRaise),
129047
- sqlite3SrcListAppend(pParse, 0, &tDb, &tFrom),
129273
+ pSrc,
129048129274
pWhere,
129049129275
0, 0, 0, 0, 0
129050129276
);
129051129277
pWhere = 0;
129052129278
}
@@ -137995,11 +138221,13 @@
137995138221
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
137996138222
if( encoding==0 ) encoding = SQLITE_UTF8;
137997138223
#else
137998138224
encoding = SQLITE_UTF8;
137999138225
#endif
138000
- if( db->nVdbeActive>0 && encoding!=ENC(db) ){
138226
+ if( db->nVdbeActive>0 && encoding!=ENC(db)
138227
+ && (db->mDbFlags & DBFLAG_Vacuum)==0
138228
+ ){
138001138229
rc = SQLITE_LOCKED;
138002138230
goto initone_error_out;
138003138231
}else{
138004138232
sqlite3SetTextEncoding(db, encoding);
138005138233
}
@@ -138389,11 +138617,15 @@
138389138617
sParse.pOuterParse = db->pParse;
138390138618
db->pParse = &sParse;
138391138619
sParse.db = db;
138392138620
sParse.pReprepare = pReprepare;
138393138621
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
+ }
138395138627
assert( sqlite3_mutex_held(db->mutex) );
138396138628
138397138629
/* For a long-term use prepared statement avoid the use of
138398138630
** lookaside memory.
138399138631
*/
@@ -139478,11 +139710,11 @@
139478139710
** by a prior OP_MakeRecord. In this case nData==1 and regData
139479139711
** will be completely unrelated to regOrigData.
139480139712
** (2) All output columns are included in the sort record. In that
139481139713
** case regData==regOrigData.
139482139714
** (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
139484139716
** SQLITE_ECEL_OMITREF optimization, or due to the
139485139717
** SortCtx.pDeferredRowLoad optimiation. In any of these cases
139486139718
** regOrigData is 0 to prevent this routine from trying to copy
139487139719
** values that might not yet exist.
139488139720
*/
@@ -141079,11 +141311,11 @@
141079141311
141080141312
assert( pSelect!=0 );
141081141313
assert( (pSelect->selFlags & SF_Resolved)!=0 );
141082141314
assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
141083141315
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
141084
- if( db->mallocFailed ) return;
141316
+ if( db->mallocFailed || IN_RENAME_OBJECT ) return;
141085141317
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
141086141318
a = pSelect->pEList->a;
141087141319
memset(&sNC, 0, sizeof(sNC));
141088141320
sNC.pSrcList = pSelect->pSrc;
141089141321
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
@@ -141124,22 +141356,20 @@
141124141356
if( sqlite3StdTypeAffinity[j]==pCol->affinity ){
141125141357
zType = sqlite3StdType[j];
141126141358
break;
141127141359
}
141128141360
}
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;
141141141371
}
141142141372
}
141143141373
pColl = sqlite3ExprCollSeq(pParse, p);
141144141374
if( pColl ){
141145141375
assert( pTab->pIndex==0 );
@@ -143870,25 +144100,50 @@
143870144100
**
143871144101
** (8) If the subquery is a compound that uses UNION, INTERSECT,
143872144102
** or EXCEPT, then all of the result set columns for all arms of
143873144103
** the compound must use the BINARY collating sequence.
143874144104
**
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
143875144123
**
143876144124
** Return 0 if no changes are made and non-zero if one or more WHERE clause
143877144125
** terms are duplicated into the subquery.
143878144126
*/
143879144127
static int pushDownWhereTerms(
143880144128
Parse *pParse, /* Parse context (for malloc() and error reporting) */
143881144129
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
143882144130
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 */
143884144133
){
143885144134
Expr *pNew;
144135
+ SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */
143886144136
int nChng = 0;
144137
+ pSrc = &pSrcList->a[iSrc];
143887144138
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
+ }
143890144145
143891144146
if( pSubq->pPrior ){
143892144147
Select *pSel;
143893144148
int notUnionAll = 0;
143894144149
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
@@ -143939,15 +144194,32 @@
143939144194
143940144195
if( pSubq->pLimit!=0 ){
143941144196
return 0; /* restriction (3) */
143942144197
}
143943144198
while( pWhere->op==TK_AND ){
143944
- nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc);
144199
+ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc);
143945144200
pWhere = pWhere->pLeft;
143946144201
}
143947144202
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
+ }
143949144221
if( isLeftJoin
143950144222
&& (ExprHasProperty(pWhere,EP_OuterON)==0
143951144223
|| pWhere->w.iJoin!=iCursor)
143952144224
){
143953144225
return 0; /* restriction (4) */
@@ -143957,11 +144229,11 @@
143957144229
){
143958144230
return 0; /* restriction (5) */
143959144231
}
143960144232
#endif
143961144233
143962
- if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){
144234
+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
143963144235
nChng++;
143964144236
pSubq->selFlags |= SF_PushDown;
143965144237
while( pSubq ){
143966144238
SubstContext x;
143967144239
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
@@ -144027,15 +144299,17 @@
144027144299
if( pX->pPrior && pX->op!=TK_ALL ){
144028144300
/* This optimization does not work for compound subqueries that
144029144301
** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */
144030144302
return 0;
144031144303
}
144304
+#ifndef SQLITE_OMIT_WINDOWFUNC
144032144305
if( pX->pWin ){
144033144306
/* This optimization does not work for subqueries that use window
144034144307
** functions. */
144035144308
return 0;
144036144309
}
144310
+#endif
144037144311
}
144038144312
colUsed = pItem->colUsed;
144039144313
if( pSub->pOrderBy ){
144040144314
ExprList *pList = pSub->pOrderBy;
144041144315
for(j=0; j<pList->nExpr; j++){
@@ -145207,16 +145481,17 @@
145207145481
assert( pAggInfo->iFirstReg==0 );
145208145482
assert( pSelect!=0 );
145209145483
assert( pSelect->pGroupBy!=0 );
145210145484
pAggInfo->nColumn = pAggInfo->nAccumulator;
145211145485
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;
145217145491
}
145492
+ pAggInfo->nSortingColumn = mx+1;
145218145493
}
145219145494
analyzeAggFuncArgs(pAggInfo, pNC);
145220145495
#if TREETRACE_ENABLED
145221145496
if( sqlite3TreeTrace & 0x20 ){
145222145497
IndexedExpr *pIEpr;
@@ -145952,11 +146227,11 @@
145952146227
assert( pItem->iCursor>=0 );
145953146228
unsetJoinExpr(p->pWhere, pItem->iCursor,
145954146229
pTabList->a[0].fg.jointype & JT_LTORJ);
145955146230
}
145956146231
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 */
145958146233
if( pSub==0 ) continue;
145959146234
145960146235
/* Catch mismatch in the declared columns of a view and the number of
145961146236
** columns in the SELECT on the RHS */
145962146237
if( pTab->nCol!=pSub->pEList->nExpr ){
@@ -146149,11 +146424,11 @@
146149146424
** inside the subquery. This can help the subquery to run more efficiently.
146150146425
*/
146151146426
if( OptimizationEnabled(db, SQLITE_PushDown)
146152146427
&& (pItem->fg.isCte==0
146153146428
|| (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)
146155146430
){
146156146431
#if TREETRACE_ENABLED
146157146432
if( sqlite3TreeTrace & 0x4000 ){
146158146433
TREETRACE(0x4000,pParse,p,
146159146434
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
@@ -151944,11 +152219,14 @@
151944152219
default:
151945152220
xMethod = pMod->xRelease;
151946152221
break;
151947152222
}
151948152223
if( xMethod && pVTab->iSavepoint>iSavepoint ){
152224
+ u64 savedFlags = (db->flags & SQLITE_Defensive);
152225
+ db->flags &= ~(u64)SQLITE_Defensive;
151949152226
rc = xMethod(pVTab->pVtab, iSavepoint);
152227
+ db->flags |= savedFlags;
151950152228
}
151951152229
sqlite3VtabUnlock(pVTab);
151952152230
}
151953152231
}
151954152232
}
@@ -153869,10 +154147,13 @@
153869154147
rc = WRC_Prune;
153870154148
reg = ++pWalker->pParse->nMem; /* Register for column value */
153871154149
reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg);
153872154150
pExpr->op = TK_REGISTER;
153873154151
pExpr->iTable = reg;
154152
+ }else if( pExpr->op==TK_TRUEFALSE ){
154153
+ /* Do not walk disabled expressions. tag-20230504-1 */
154154
+ return WRC_Prune;
153874154155
}
153875154156
return rc;
153876154157
}
153877154158
153878154159
/*
@@ -153970,11 +154251,11 @@
153970154251
/* If we survive all prior tests, that means this term is worth hinting */
153971154252
pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
153972154253
}
153973154254
if( pExpr!=0 ){
153974154255
sWalker.xExprCallback = codeCursorHintFixExpr;
153975
- sqlite3WalkExpr(&sWalker, pExpr);
154256
+ if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr);
153976154257
sqlite3VdbeAddOp4(v, OP_CursorHint,
153977154258
(sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
153978154259
(const char*)pExpr, P4_EXPR);
153979154260
}
153980154261
}
@@ -156836,11 +157117,11 @@
156836157117
if( op==TK_ISNULL
156837157118
&& !ExprHasProperty(pExpr,EP_OuterON)
156838157119
&& 0==sqlite3ExprCanBeNull(pLeft)
156839157120
){
156840157121
assert( !ExprHasProperty(pExpr, EP_IntValue) );
156841
- pExpr->op = TK_TRUEFALSE;
157122
+ pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */
156842157123
pExpr->u.zToken = "false";
156843157124
ExprSetProperty(pExpr, EP_IsFalse);
156844157125
pTerm->prereqAll = 0;
156845157126
pTerm->eOperator = 0;
156846157127
}
@@ -158368,12 +158649,11 @@
158368158649
** and to set up the WhereLevel object pLevel so that the code generator
158369158650
** makes use of the automatic index.
158370158651
*/
158371158652
static SQLITE_NOINLINE void constructAutomaticIndex(
158372158653
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 */
158375158655
const Bitmask notReady, /* Mask of cursors that are not available */
158376158656
WhereLevel *pLevel /* Write new index here */
158377158657
){
158378158658
int nKeyCol; /* Number of columns in the constructed index */
158379158659
WhereTerm *pTerm; /* A single term of the WHERE clause */
@@ -158394,11 +158674,12 @@
158394158674
Bitmask extraCols; /* Bitmap of additional columns */
158395158675
u8 sentWarning = 0; /* True if a warning has been issued */
158396158676
u8 useBloomFilter = 0; /* True to also add a Bloom filter */
158397158677
Expr *pPartial = 0; /* Partial Index Expression */
158398158678
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 */
158400158681
int addrCounter = 0; /* Address where integer counter is initialized */
158401158682
int regBase; /* Array of registers where record is assembled */
158402158683
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
158403158684
int addrExp = 0; /* Address of OP_Explain */
158404158685
#endif
@@ -158410,10 +158691,12 @@
158410158691
addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
158411158692
158412158693
/* Count the number of columns that will be added to the index
158413158694
** and used to match WHERE clause constraints */
158414158695
nKeyCol = 0;
158696
+ pTabList = pWC->pWInfo->pTabList;
158697
+ pSrc = &pTabList->a[pLevel->iFrom];
158415158698
pTable = pSrc->pTab;
158416158699
pWCEnd = &pWC->a[pWC->nTerm];
158417158700
pLoop = pLevel->pWLoop;
158418158701
idxCols = 0;
158419158702
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
@@ -158420,11 +158703,11 @@
158420158703
Expr *pExpr = pTerm->pExpr;
158421158704
/* Make the automatic index a partial index if there are terms in the
158422158705
** WHERE clause (or the ON clause of a LEFT join) that constrain which
158423158706
** rows of the target table (pSrc) that can be used. */
158424158707
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
158425
- && sqlite3ExprIsTableConstraint(pExpr, pSrc)
158708
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
158426158709
){
158427158710
pPartial = sqlite3ExprAnd(pParse, pPartial,
158428158711
sqlite3ExprDup(pParse->db, pExpr, 0));
158429158712
}
158430158713
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
@@ -158550,18 +158833,18 @@
158550158833
pLevel->regFilter = ++pParse->nMem;
158551158834
sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter);
158552158835
}
158553158836
158554158837
/* 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;
158558158841
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);
158560158843
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
158561158844
VdbeCoverage(v);
158562
- VdbeComment((v, "next row of %s", pTabItem->pTab->zName));
158845
+ VdbeComment((v, "next row of %s", pSrc->pTab->zName));
158563158846
}else{
158564158847
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
158565158848
}
158566158849
if( pPartial ){
158567158850
iContinue = sqlite3VdbeMakeLabel(pParse);
@@ -158578,18 +158861,18 @@
158578158861
}
158579158862
sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v));
158580158863
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
158581158864
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
158582158865
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
158583
- if( pTabItem->fg.viaCoroutine ){
158866
+ if( pSrc->fg.viaCoroutine ){
158584158867
sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
158585158868
testcase( pParse->db->mallocFailed );
158586158869
assert( pLevel->iIdxCur>0 );
158587158870
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
158588
- pTabItem->regResult, pLevel->iIdxCur);
158871
+ pSrc->regResult, pLevel->iIdxCur);
158589158872
sqlite3VdbeGoto(v, addrTop);
158590
- pTabItem->fg.viaCoroutine = 0;
158873
+ pSrc->fg.viaCoroutine = 0;
158591158874
}else{
158592158875
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
158593158876
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
158594158877
}
158595158878
sqlite3VdbeJumpHere(v, addrTop);
@@ -158648,13 +158931,15 @@
158648158931
assert( v!=0 );
158649158932
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
158650158933
158651158934
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
158652158935
do{
158936
+ const SrcList *pTabList;
158653158937
const SrcItem *pItem;
158654158938
const Table *pTab;
158655158939
u64 sz;
158940
+ int iSrc;
158656158941
sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
158657158942
addrCont = sqlite3VdbeMakeLabel(pParse);
158658158943
iCur = pLevel->iTabCur;
158659158944
pLevel->regFilter = ++pParse->nMem;
158660158945
@@ -158664,11 +158949,13 @@
158664158949
** measure the size of the table at run-time using OP_Count with
158665158950
** P3==1 and use that value to initialize the blob. But that makes
158666158951
** testing complicated. By basing the blob size on the value in the
158667158952
** sqlite_stat1 table, testing is much easier.
158668158953
*/
158669
- pItem = &pWInfo->pTabList->a[pLevel->iFrom];
158954
+ pTabList = pWInfo->pTabList;
158955
+ iSrc = pLevel->iFrom;
158956
+ pItem = &pTabList->a[iSrc];
158670158957
assert( pItem!=0 );
158671158958
pTab = pItem->pTab;
158672158959
assert( pTab!=0 );
158673158960
sz = sqlite3LogEstToInt(pTab->nRowLogEst);
158674158961
if( sz<10000 ){
@@ -158681,11 +158968,11 @@
158681158968
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
158682158969
pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
158683158970
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
158684158971
Expr *pExpr = pTerm->pExpr;
158685158972
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
158686
- && sqlite3ExprIsTableConstraint(pExpr, pItem)
158973
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
158687158974
){
158688158975
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
158689158976
}
158690158977
}
158691158978
if( pLoop->wsFlags & WHERE_IPK ){
@@ -163020,10 +163307,17 @@
163020163307
** 3) Either the query must be DISTINCT, or else the ON or USING clause
163021163308
** must contain a constraint that limits the scan of the table to
163022163309
** at most a single row.
163023163310
** 4) The table must not be referenced by any part of the query apart
163024163311
** 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.
163025163319
**
163026163320
** For example, given:
163027163321
**
163028163322
** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1);
163029163323
** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2);
@@ -163045,10 +163339,11 @@
163045163339
WhereInfo *pWInfo,
163046163340
Bitmask notReady
163047163341
){
163048163342
int i;
163049163343
Bitmask tabUsed;
163344
+ int hasRightJoin;
163050163345
163051163346
/* Preconditions checked by the caller */
163052163347
assert( pWInfo->nLevel>=2 );
163053163348
assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) );
163054163349
@@ -163059,10 +163354,11 @@
163059163354
163060163355
tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet);
163061163356
if( pWInfo->pOrderBy ){
163062163357
tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
163063163358
}
163359
+ hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0;
163064163360
for(i=pWInfo->nLevel-1; i>=1; i--){
163065163361
WhereTerm *pTerm, *pEnd;
163066163362
SrcItem *pItem;
163067163363
WhereLoop *pLoop;
163068163364
pLoop = pWInfo->a[i].pWLoop;
@@ -163081,10 +163377,16 @@
163081163377
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
163082163378
){
163083163379
break;
163084163380
}
163085163381
}
163382
+ if( hasRightJoin
163383
+ && ExprHasProperty(pTerm->pExpr, EP_InnerON)
163384
+ && pTerm->pExpr->w.iJoin==pItem->iCursor
163385
+ ){
163386
+ break; /* restriction (5) */
163387
+ }
163086163388
}
163087163389
if( pTerm<pEnd ) continue;
163088163390
WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId));
163089163391
notReady &= ~pLoop->maskSelf;
163090163392
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
@@ -163480,26 +163782,49 @@
163480163782
if( pSelect && pSelect->pLimit ){
163481163783
sqlite3WhereAddLimit(&pWInfo->sWC, pSelect);
163482163784
}
163483163785
if( pParse->nErr ) goto whereBeginError;
163484163786
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.
163495163812
*/
163496163813
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 */
163498163816
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);
163501163826
pT->wtFlags |= TERM_CODED;
163502163827
}
163503163828
}
163504163829
163505163830
if( wctrlFlags & WHERE_WANT_DISTINCT ){
@@ -163875,15 +164200,15 @@
163875164200
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
163876164201
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
163877164202
sqlite3VdbeJumpHere(v, iOnce);
163878164203
}
163879164204
}
164205
+ assert( pTabList == pWInfo->pTabList );
163880164206
if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){
163881164207
if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){
163882164208
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
163883
- constructAutomaticIndex(pParse, &pWInfo->sWC,
163884
- &pTabList->a[pLevel->iFrom], notReady, pLevel);
164209
+ constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel);
163885164210
#endif
163886164211
}else{
163887164212
sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady);
163888164213
}
163889164214
if( db->mallocFailed ) goto whereBeginError;
@@ -165072,10 +165397,11 @@
165072165397
}
165073165398
}
165074165399
}
165075165400
/* no break */ deliberate_fall_through
165076165401
165402
+ case TK_IF_NULL_ROW:
165077165403
case TK_AGG_FUNCTION:
165078165404
case TK_COLUMN: {
165079165405
int iCol = -1;
165080165406
if( pParse->db->mallocFailed ) return WRC_Abort;
165081165407
if( p->pSub ){
@@ -167902,11 +168228,11 @@
167902168228
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
167903168229
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
167904168230
#define YYFALLBACK 1
167905168231
#define YYNSTATE 575
167906168232
#define YYNRULE 403
167907
-#define YYNRULE_WITH_ACTION 341
168233
+#define YYNRULE_WITH_ACTION 340
167908168234
#define YYNTOKEN 185
167909168235
#define YY_MAX_SHIFT 574
167910168236
#define YY_MIN_SHIFTREDUCE 833
167911168237
#define YY_MAX_SHIFTREDUCE 1235
167912168238
#define YY_ERROR_ACTION 1236
@@ -167982,145 +168308,145 @@
167982168308
*********** Begin parsing tables **********************************************/
167983168309
#define YY_ACTTAB_COUNT (2096)
167984168310
static const YYACTIONTYPE yy_action[] = {
167985168311
/* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229,
167986168312
/* 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,
167988168314
/* 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,
167990168316
/* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409,
167991168317
/* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229,
167992168318
/* 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,
167994168320
/* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115,
167995168321
/* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120,
167996168322
/* 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,
167999168325
/* 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,
168001168327
/* 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,
168004168330
/* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122,
168005168331
/* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113,
168006168332
/* 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,
168009168335
/* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
168010168336
/* 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,
168013168339
/* 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,
168016168342
/* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122,
168017168343
/* 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,
168019168345
/* 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,
168021168347
/* 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,
168026168352
/* 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,
168028168354
/* 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,
168030168356
/* 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,
168032168358
/* 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,
168041168367
/* 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,
168046168372
/* 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,
168052168378
/* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
168053168379
/* 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,
168057168383
/* 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,
168063168389
/* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
168064168390
/* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121,
168065168391
/* 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,
168068168394
/* 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,
168074168400
/* 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,
168076168402
/* 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,
168079168405
/* 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,
168083168409
/* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637,
168084168410
/* 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,
168086168412
/* 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,
168091168417
/* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
168092168418
/* 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,
168095168421
/* 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,
168103168429
/* 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,
168106168432
/* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121,
168107168433
/* 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,
168109168435
/* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457,
168110168436
/* 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,
168117168443
/* 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,
168119168445
/* 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,
168122168448
/* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62,
168123168449
/* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49,
168124168450
/* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63,
168125168451
/* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534,
168126168452
/* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024,
@@ -168127,73 +168453,73 @@
168127168453
/* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66,
168128168454
/* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177,
168129168455
/* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471,
168130168456
/* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407,
168131168457
/* 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,
168134168460
/* 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,
168136168462
/* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163,
168137168463
/* 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,
168140168466
/* 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,
168142168468
/* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568,
168143168469
/* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130,
168144168470
/* 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,
168146168472
/* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568,
168147168473
/* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355,
168148168474
/* 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,
168150168476
/* 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,
168152168478
/* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261,
168153168479
/* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74,
168154168480
/* 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,
168156168482
/* 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,
168158168484
/* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219,
168159168485
/* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110,
168160168486
/* 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,
168165168491
/* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461,
168166168492
/* 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,
168170168496
/* 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,
168174168500
/* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10,
168175168501
/* 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,
168179168505
/* 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,
168182168508
/* 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,
168185168511
/* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19,
168186168512
/* 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,
168193168519
/* 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,
168195168521
};
168196168522
static const YYCODETYPE yy_lookahead[] = {
168197168523
/* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
168198168524
/* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19,
168199168525
/* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216,
@@ -168532,67 +168858,67 @@
168532168858
/* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718,
168533168859
/* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716,
168534168860
/* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740,
168535168861
};
168536168862
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,
168541168867
/* 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,
168545168871
/* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168546168872
/* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168547168873
/* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168548168874
/* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168549168875
/* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168550168876
/* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436,
168551168877
/* 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,
168555168881
/* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168556168882
/* 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,
168559168885
/* 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,
168561168887
/* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168562168888
/* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168563168889
/* 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,
168565168891
/* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633,
168566168892
/* 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,
168568168894
/* 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,
168579168905
/* 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,
168582168908
/* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358,
168583168909
/* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236,
168584168910
/* 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,
168586168912
/* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168587168913
/* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236,
168588168914
/* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
168589168915
/* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382,
168590168916
/* 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,
168592168918
/* 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,
168594168920
/* 570 */ 1258, 1236, 1248, 1236, 1236,
168595168921
};
168596168922
/********** End of lemon-generated parsing tables *****************************/
168597168923
168598168924
/* The next table maps tokens (terminal symbols) into fallback tokens.
@@ -169437,162 +169763,162 @@
169437169763
/* 224 */ "expr ::= CASE case_operand case_exprlist case_else END",
169438169764
/* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
169439169765
/* 226 */ "case_exprlist ::= WHEN expr THEN expr",
169440169766
/* 227 */ "case_else ::= ELSE expr",
169441169767
/* 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",
169594169920
/* 381 */ "exprlist ::= nexprlist",
169595169921
/* 382 */ "nmnum ::= plus_num",
169596169922
/* 383 */ "nmnum ::= nm",
169597169923
/* 384 */ "nmnum ::= ON",
169598169924
/* 385 */ "nmnum ::= DELETE",
@@ -170346,162 +170672,162 @@
170346170672
217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
170347170673
279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
170348170674
279, /* (226) case_exprlist ::= WHEN expr THEN expr */
170349170675
280, /* (227) case_else ::= ELSE expr */
170350170676
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 */
170503170829
261, /* (381) exprlist ::= nexprlist */
170504170830
284, /* (382) nmnum ::= plus_num */
170505170831
284, /* (383) nmnum ::= nm */
170506170832
284, /* (384) nmnum ::= ON */
170507170833
284, /* (385) nmnum ::= DELETE */
@@ -170754,162 +171080,162 @@
170754171080
-5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
170755171081
-5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
170756171082
-4, /* (226) case_exprlist ::= WHEN expr THEN expr */
170757171083
-2, /* (227) case_else ::= ELSE expr */
170758171084
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 */
170911171237
-1, /* (381) exprlist ::= nexprlist */
170912171238
-1, /* (382) nmnum ::= plus_num */
170913171239
-1, /* (383) nmnum ::= nm */
170914171240
-1, /* (384) nmnum ::= ON */
170915171241
-1, /* (385) nmnum ::= DELETE */
@@ -170987,11 +171313,11 @@
170987171313
{yymsp[1].minor.yy394 = TK_DEFERRED;}
170988171314
break;
170989171315
case 5: /* transtype ::= DEFERRED */
170990171316
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
170991171317
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);
170993171319
{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/}
170994171320
break;
170995171321
case 8: /* cmd ::= COMMIT|END trans_opt */
170996171322
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
170997171323
{sqlite3EndTransaction(pParse,yymsp[-1].major);}
@@ -171024,11 +171350,11 @@
171024171350
case 47: /* autoinc ::= */ yytestcase(yyruleno==47);
171025171351
case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62);
171026171352
case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
171027171353
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
171028171354
case 98: /* distinct ::= */ yytestcase(yyruleno==98);
171029
- case 243: /* collate ::= */ yytestcase(yyruleno==243);
171355
+ case 242: /* collate ::= */ yytestcase(yyruleno==242);
171030171356
{yymsp[1].minor.yy394 = 0;}
171031171357
break;
171032171358
case 16: /* ifnotexists ::= IF NOT EXISTS */
171033171359
{yymsp[-2].minor.yy394 = 1;}
171034171360
break;
@@ -171210,11 +171536,11 @@
171210171536
break;
171211171537
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
171212171538
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
171213171539
case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215);
171214171540
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);
171216171542
{yymsp[-1].minor.yy394 = 1;}
171217171543
break;
171218171544
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
171219171545
{yymsp[-1].minor.yy394 = 0;}
171220171546
break;
@@ -171360,13 +171686,13 @@
171360171686
{yymsp[0].minor.yy394 = SF_All;}
171361171687
break;
171362171688
case 99: /* sclp ::= */
171363171689
case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
171364171690
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);
171368171694
{yymsp[1].minor.yy322 = 0;}
171369171695
break;
171370171696
case 100: /* selcollist ::= sclp scanpt expr scanpt as */
171371171697
{
171372171698
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
@@ -171388,12 +171714,12 @@
171388171714
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
171389171715
}
171390171716
break;
171391171717
case 103: /* as ::= AS nm */
171392171718
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);
171395171721
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
171396171722
break;
171397171723
case 105: /* from ::= */
171398171724
case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
171399171725
{yymsp[1].minor.yy131 = 0;}
@@ -171433,11 +171759,11 @@
171433171759
break;
171434171760
case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
171435171761
{
171436171762
if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
171437171763
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 ){
171439171765
yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
171440171766
if( yymsp[-5].minor.yy131 ){
171441171767
SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1];
171442171768
SrcItem *pOld = yymsp[-3].minor.yy131->a;
171443171769
pNew->zName = pOld->zName;
@@ -171562,19 +171888,19 @@
171562171888
case 144: /* having_opt ::= */
171563171889
case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
171564171890
case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
171565171891
case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
171566171892
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);
171569171895
{yymsp[1].minor.yy528 = 0;}
171570171896
break;
171571171897
case 145: /* having_opt ::= HAVING expr */
171572171898
case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
171573171899
case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
171574171900
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);
171576171902
{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
171577171903
break;
171578171904
case 147: /* limit_opt ::= LIMIT expr */
171579171905
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);}
171580171906
break;
@@ -172001,394 +172327,391 @@
172001172327
{
172002172328
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
172003172329
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
172004172330
}
172005172331
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 */
172010172333
{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
172011172334
break;
172012
- case 233: /* nexprlist ::= expr */
172335
+ case 232: /* nexprlist ::= expr */
172013172336
{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
172014172337
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);
172017172340
{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
172018172341
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 */
172020172343
{
172021172344
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
172022172345
sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394,
172023172346
&yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF);
172024172347
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
172025172348
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
172026172349
}
172027172350
}
172028172351
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);
172031172354
{yymsp[0].minor.yy394 = OE_Abort;}
172032172355
break;
172033
- case 238: /* uniqueflag ::= */
172356
+ case 237: /* uniqueflag ::= */
172034172357
{yymsp[1].minor.yy394 = OE_None;}
172035172358
break;
172036
- case 241: /* eidlist ::= eidlist COMMA nm collate sortorder */
172359
+ case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */
172037172360
{
172038172361
yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
172039172362
}
172040172363
break;
172041
- case 242: /* eidlist ::= nm collate sortorder */
172364
+ case 241: /* eidlist ::= nm collate sortorder */
172042172365
{
172043172366
yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
172044172367
}
172045172368
break;
172046
- case 245: /* cmd ::= DROP INDEX ifexists fullname */
172369
+ case 244: /* cmd ::= DROP INDEX ifexists fullname */
172047172370
{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
172048172371
break;
172049
- case 246: /* cmd ::= VACUUM vinto */
172372
+ case 245: /* cmd ::= VACUUM vinto */
172050172373
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
172051172374
break;
172052
- case 247: /* cmd ::= VACUUM nm vinto */
172375
+ case 246: /* cmd ::= VACUUM nm vinto */
172053172376
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
172054172377
break;
172055
- case 250: /* cmd ::= PRAGMA nm dbnm */
172378
+ case 249: /* cmd ::= PRAGMA nm dbnm */
172056172379
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
172057172380
break;
172058
- case 251: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
172381
+ case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
172059172382
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
172060172383
break;
172061
- case 252: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
172384
+ case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
172062172385
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
172063172386
break;
172064
- case 253: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
172387
+ case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
172065172388
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
172066172389
break;
172067
- case 254: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
172390
+ case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
172068172391
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
172069172392
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 */
172071172394
{
172072172395
Token all;
172073172396
all.z = yymsp[-3].minor.yy0.z;
172074172397
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
172075172398
sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all);
172076172399
}
172077172400
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 */
172079172402
{
172080172403
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);
172081172404
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
172082172405
}
172083172406
break;
172084
- case 259: /* trigger_time ::= BEFORE|AFTER */
172407
+ case 258: /* trigger_time ::= BEFORE|AFTER */
172085172408
{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
172086172409
break;
172087
- case 260: /* trigger_time ::= INSTEAD OF */
172410
+ case 259: /* trigger_time ::= INSTEAD OF */
172088172411
{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
172089172412
break;
172090
- case 261: /* trigger_time ::= */
172413
+ case 260: /* trigger_time ::= */
172091172414
{ yymsp[1].minor.yy394 = TK_BEFORE; }
172092172415
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);
172095172418
{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
172096172419
break;
172097
- case 264: /* trigger_event ::= UPDATE OF idlist */
172420
+ case 263: /* trigger_event ::= UPDATE OF idlist */
172098172421
{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
172099172422
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);
172102172425
{ yymsp[1].minor.yy528 = 0; }
172103172426
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);
172106172429
{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
172107172430
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 */
172109172432
{
172110172433
assert( yymsp[-2].minor.yy33!=0 );
172111172434
yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
172112172435
yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
172113172436
}
172114172437
break;
172115
- case 268: /* trigger_cmd_list ::= trigger_cmd SEMI */
172438
+ case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */
172116172439
{
172117172440
assert( yymsp[-1].minor.yy33!=0 );
172118172441
yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
172119172442
}
172120172443
break;
172121
- case 269: /* trnm ::= nm DOT nm */
172444
+ case 268: /* trnm ::= nm DOT nm */
172122172445
{
172123172446
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
172124172447
sqlite3ErrorMsg(pParse,
172125172448
"qualified table names are not allowed on INSERT, UPDATE, and DELETE "
172126172449
"statements within triggers");
172127172450
}
172128172451
break;
172129
- case 270: /* tridxby ::= INDEXED BY nm */
172452
+ case 269: /* tridxby ::= INDEXED BY nm */
172130172453
{
172131172454
sqlite3ErrorMsg(pParse,
172132172455
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
172133172456
"within triggers");
172134172457
}
172135172458
break;
172136
- case 271: /* tridxby ::= NOT INDEXED */
172459
+ case 270: /* tridxby ::= NOT INDEXED */
172137172460
{
172138172461
sqlite3ErrorMsg(pParse,
172139172462
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
172140172463
"within triggers");
172141172464
}
172142172465
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 */
172144172467
{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);}
172145172468
yymsp[-8].minor.yy33 = yylhsminor.yy33;
172146172469
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 */
172148172471
{
172149172472
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*/
172150172473
}
172151172474
yymsp[-7].minor.yy33 = yylhsminor.yy33;
172152172475
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 */
172154172477
{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
172155172478
yymsp[-5].minor.yy33 = yylhsminor.yy33;
172156172479
break;
172157
- case 275: /* trigger_cmd ::= scanpt select scanpt */
172480
+ case 274: /* trigger_cmd ::= scanpt select scanpt */
172158172481
{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
172159172482
yymsp[-2].minor.yy33 = yylhsminor.yy33;
172160172483
break;
172161
- case 276: /* expr ::= RAISE LP IGNORE RP */
172484
+ case 275: /* expr ::= RAISE LP IGNORE RP */
172162172485
{
172163172486
yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
172164172487
if( yymsp[-3].minor.yy528 ){
172165172488
yymsp[-3].minor.yy528->affExpr = OE_Ignore;
172166172489
}
172167172490
}
172168172491
break;
172169
- case 277: /* expr ::= RAISE LP raisetype COMMA nm RP */
172492
+ case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */
172170172493
{
172171172494
yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
172172172495
if( yymsp[-5].minor.yy528 ) {
172173172496
yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394;
172174172497
}
172175172498
}
172176172499
break;
172177
- case 278: /* raisetype ::= ROLLBACK */
172500
+ case 277: /* raisetype ::= ROLLBACK */
172178172501
{yymsp[0].minor.yy394 = OE_Rollback;}
172179172502
break;
172180
- case 280: /* raisetype ::= FAIL */
172503
+ case 279: /* raisetype ::= FAIL */
172181172504
{yymsp[0].minor.yy394 = OE_Fail;}
172182172505
break;
172183
- case 281: /* cmd ::= DROP TRIGGER ifexists fullname */
172506
+ case 280: /* cmd ::= DROP TRIGGER ifexists fullname */
172184172507
{
172185172508
sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
172186172509
}
172187172510
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 */
172189172512
{
172190172513
sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
172191172514
}
172192172515
break;
172193
- case 283: /* cmd ::= DETACH database_kw_opt expr */
172516
+ case 282: /* cmd ::= DETACH database_kw_opt expr */
172194172517
{
172195172518
sqlite3Detach(pParse, yymsp[0].minor.yy528);
172196172519
}
172197172520
break;
172198
- case 286: /* cmd ::= REINDEX */
172521
+ case 285: /* cmd ::= REINDEX */
172199172522
{sqlite3Reindex(pParse, 0, 0);}
172200172523
break;
172201
- case 287: /* cmd ::= REINDEX nm dbnm */
172524
+ case 286: /* cmd ::= REINDEX nm dbnm */
172202172525
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
172203172526
break;
172204
- case 288: /* cmd ::= ANALYZE */
172527
+ case 287: /* cmd ::= ANALYZE */
172205172528
{sqlite3Analyze(pParse, 0, 0);}
172206172529
break;
172207
- case 289: /* cmd ::= ANALYZE nm dbnm */
172530
+ case 288: /* cmd ::= ANALYZE nm dbnm */
172208172531
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
172209172532
break;
172210
- case 290: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
172533
+ case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
172211172534
{
172212172535
sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
172213172536
}
172214172537
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 */
172216172539
{
172217172540
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
172218172541
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
172219172542
}
172220172543
break;
172221
- case 292: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
172544
+ case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
172222172545
{
172223172546
sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
172224172547
}
172225172548
break;
172226
- case 293: /* add_column_fullname ::= fullname */
172549
+ case 292: /* add_column_fullname ::= fullname */
172227172550
{
172228172551
disableLookaside(pParse);
172229172552
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
172230172553
}
172231172554
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 */
172233172556
{
172234172557
sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
172235172558
}
172236172559
break;
172237
- case 295: /* cmd ::= create_vtab */
172560
+ case 294: /* cmd ::= create_vtab */
172238172561
{sqlite3VtabFinishParse(pParse,0);}
172239172562
break;
172240
- case 296: /* cmd ::= create_vtab LP vtabarglist RP */
172563
+ case 295: /* cmd ::= create_vtab LP vtabarglist RP */
172241172564
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
172242172565
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 */
172244172567
{
172245172568
sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394);
172246172569
}
172247172570
break;
172248
- case 298: /* vtabarg ::= */
172571
+ case 297: /* vtabarg ::= */
172249172572
{sqlite3VtabArgInit(pParse);}
172250172573
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);
172254172577
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
172255172578
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);
172258172581
{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
172259172582
break;
172260
- case 304: /* wqas ::= AS */
172583
+ case 303: /* wqas ::= AS */
172261172584
{yymsp[0].minor.yy516 = M10d_Any;}
172262172585
break;
172263
- case 305: /* wqas ::= AS MATERIALIZED */
172586
+ case 304: /* wqas ::= AS MATERIALIZED */
172264172587
{yymsp[-1].minor.yy516 = M10d_Yes;}
172265172588
break;
172266
- case 306: /* wqas ::= AS NOT MATERIALIZED */
172589
+ case 305: /* wqas ::= AS NOT MATERIALIZED */
172267172590
{yymsp[-2].minor.yy516 = M10d_No;}
172268172591
break;
172269
- case 307: /* wqitem ::= nm eidlist_opt wqas LP select RP */
172592
+ case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */
172270172593
{
172271172594
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*/
172272172595
}
172273172596
break;
172274
- case 308: /* wqlist ::= wqitem */
172597
+ case 307: /* wqlist ::= wqitem */
172275172598
{
172276172599
yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
172277172600
}
172278172601
break;
172279
- case 309: /* wqlist ::= wqlist COMMA wqitem */
172602
+ case 308: /* wqlist ::= wqlist COMMA wqitem */
172280172603
{
172281172604
yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
172282172605
}
172283172606
break;
172284
- case 310: /* windowdefn_list ::= windowdefn */
172607
+ case 309: /* windowdefn_list ::= windowdefn */
172285172608
{ yylhsminor.yy41 = yymsp[0].minor.yy41; }
172286172609
yymsp[0].minor.yy41 = yylhsminor.yy41;
172287172610
break;
172288
- case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
172611
+ case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
172289172612
{
172290172613
assert( yymsp[0].minor.yy41!=0 );
172291172614
sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
172292172615
yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41;
172293172616
yylhsminor.yy41 = yymsp[0].minor.yy41;
172294172617
}
172295172618
yymsp[-2].minor.yy41 = yylhsminor.yy41;
172296172619
break;
172297
- case 312: /* windowdefn ::= nm AS LP window RP */
172620
+ case 311: /* windowdefn ::= nm AS LP window RP */
172298172621
{
172299172622
if( ALWAYS(yymsp[-1].minor.yy41) ){
172300172623
yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
172301172624
}
172302172625
yylhsminor.yy41 = yymsp[-1].minor.yy41;
172303172626
}
172304172627
yymsp[-4].minor.yy41 = yylhsminor.yy41;
172305172628
break;
172306
- case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
172629
+ case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
172307172630
{
172308172631
yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
172309172632
}
172310172633
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 */
172312172635
{
172313172636
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
172314172637
}
172315172638
yymsp[-5].minor.yy41 = yylhsminor.yy41;
172316172639
break;
172317
- case 315: /* window ::= ORDER BY sortlist frame_opt */
172640
+ case 314: /* window ::= ORDER BY sortlist frame_opt */
172318172641
{
172319172642
yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
172320172643
}
172321172644
break;
172322
- case 316: /* window ::= nm ORDER BY sortlist frame_opt */
172645
+ case 315: /* window ::= nm ORDER BY sortlist frame_opt */
172323172646
{
172324172647
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
172325172648
}
172326172649
yymsp[-4].minor.yy41 = yylhsminor.yy41;
172327172650
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);
172330172653
{
172331172654
yylhsminor.yy41 = yymsp[0].minor.yy41;
172332172655
}
172333172656
yymsp[0].minor.yy41 = yylhsminor.yy41;
172334172657
break;
172335
- case 318: /* window ::= nm frame_opt */
172658
+ case 317: /* window ::= nm frame_opt */
172336172659
{
172337172660
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
172338172661
}
172339172662
yymsp[-1].minor.yy41 = yylhsminor.yy41;
172340172663
break;
172341
- case 319: /* frame_opt ::= */
172664
+ case 318: /* frame_opt ::= */
172342172665
{
172343172666
yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
172344172667
}
172345172668
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 */
172347172670
{
172348172671
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);
172349172672
}
172350172673
yymsp[-2].minor.yy41 = yylhsminor.yy41;
172351172674
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 */
172353172676
{
172354172677
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);
172355172678
}
172356172679
yymsp[-5].minor.yy41 = yylhsminor.yy41;
172357172680
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);
172360172683
{yylhsminor.yy595 = yymsp[0].minor.yy595;}
172361172684
yymsp[0].minor.yy595 = yylhsminor.yy595;
172362172685
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);
172366172689
{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
172367172690
yymsp[-1].minor.yy595 = yylhsminor.yy595;
172368172691
break;
172369
- case 327: /* frame_bound ::= expr PRECEDING|FOLLOWING */
172692
+ case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */
172370172693
{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
172371172694
yymsp[-1].minor.yy595 = yylhsminor.yy595;
172372172695
break;
172373
- case 329: /* frame_exclude_opt ::= */
172696
+ case 328: /* frame_exclude_opt ::= */
172374172697
{yymsp[1].minor.yy516 = 0;}
172375172698
break;
172376
- case 330: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
172699
+ case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
172377172700
{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
172378172701
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);
172381172704
{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
172382172705
break;
172383
- case 333: /* frame_exclude ::= GROUP|TIES */
172706
+ case 332: /* frame_exclude ::= GROUP|TIES */
172384172707
{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
172385172708
break;
172386
- case 334: /* window_clause ::= WINDOW windowdefn_list */
172709
+ case 333: /* window_clause ::= WINDOW windowdefn_list */
172387172710
{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
172388172711
break;
172389
- case 335: /* filter_over ::= filter_clause over_clause */
172712
+ case 334: /* filter_over ::= filter_clause over_clause */
172390172713
{
172391172714
if( yymsp[0].minor.yy41 ){
172392172715
yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
172393172716
}else{
172394172717
sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
@@ -172395,11 +172718,11 @@
172395172718
}
172396172719
yylhsminor.yy41 = yymsp[0].minor.yy41;
172397172720
}
172398172721
yymsp[-1].minor.yy41 = yylhsminor.yy41;
172399172722
break;
172400
- case 337: /* filter_over ::= filter_clause */
172723
+ case 336: /* filter_over ::= filter_clause */
172401172724
{
172402172725
yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
172403172726
if( yylhsminor.yy41 ){
172404172727
yylhsminor.yy41->eFrmType = TK_FILTER;
172405172728
yylhsminor.yy41->pFilter = yymsp[0].minor.yy528;
@@ -172407,68 +172730,69 @@
172407172730
sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528);
172408172731
}
172409172732
}
172410172733
yymsp[0].minor.yy41 = yylhsminor.yy41;
172411172734
break;
172412
- case 338: /* over_clause ::= OVER LP window RP */
172735
+ case 337: /* over_clause ::= OVER LP window RP */
172413172736
{
172414172737
yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
172415172738
assert( yymsp[-3].minor.yy41!=0 );
172416172739
}
172417172740
break;
172418
- case 339: /* over_clause ::= OVER nm */
172741
+ case 338: /* over_clause ::= OVER nm */
172419172742
{
172420172743
yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
172421172744
if( yymsp[-1].minor.yy41 ){
172422172745
yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
172423172746
}
172424172747
}
172425172748
break;
172426
- case 340: /* filter_clause ::= FILTER LP WHERE expr RP */
172749
+ case 339: /* filter_clause ::= FILTER LP WHERE expr RP */
172427172750
{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
172428172751
break;
172429172752
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);
172470172794
/* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381);
172471172795
/* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382);
172472172796
/* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383);
172473172797
/* (384) nmnum ::= ON */ yytestcase(yyruleno==384);
172474172798
/* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385);
@@ -178496,11 +178820,11 @@
178496178820
if( pVfs==0 ) return 0;
178497178821
178498178822
/* This function works in milliseconds, but the underlying OsSleep()
178499178823
** API uses microseconds. Hence the 1000's.
178500178824
*/
178501
- rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000);
178825
+ rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000);
178502178826
return rc;
178503178827
}
178504178828
178505178829
/*
178506178830
** Enable or disable the extended result codes.
@@ -199500,10 +199824,11 @@
199500199824
#define JNODE_REMOVE 0x04 /* Do not output */
199501199825
#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
199502199826
#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
199503199827
#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
199504199828
#define JNODE_LABEL 0x40 /* Is a label of an object */
199829
+#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */
199505199830
199506199831
199507199832
/* A single node of parsed JSON
199508199833
*/
199509199834
struct JsonNode {
@@ -199526,25 +199851,27 @@
199526199851
u32 nNode; /* Number of slots of aNode[] used */
199527199852
u32 nAlloc; /* Number of slots of aNode[] allocated */
199528199853
JsonNode *aNode; /* Array of nodes containing the parse */
199529199854
const char *zJson; /* Original JSON string */
199530199855
u32 *aUp; /* Index of parent of each node */
199531
- u8 oom; /* Set to true if out of memory */
199856
+ u16 iDepth; /* Nesting depth */
199532199857
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 */
199534199860
int nJson; /* Length of the zJson string in bytes */
199861
+ u32 iErr; /* Error location in zJson[] */
199535199862
u32 iHold; /* Replace cache line with the lowest iHold value */
199536199863
};
199537199864
199538199865
/*
199539199866
** Maximum nesting depth of JSON for this implementation.
199540199867
**
199541199868
** 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
199544199871
*/
199545
-#define JSON_MAX_DEPTH 2000
199872
+#define JSON_MAX_DEPTH 1000
199546199873
199547199874
/**************************************************************************
199548199875
** Utility routines for dealing with JsonString objects
199549199876
**************************************************************************/
199550199877
@@ -199689,10 +200016,133 @@
199689200016
p->zBuf[p->nUsed++] = c;
199690200017
}
199691200018
p->zBuf[p->nUsed++] = '"';
199692200019
assert( p->nUsed<p->nAlloc );
199693200020
}
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
+
199694200144
199695200145
/*
199696200146
** Append a function parameter value to the JSON string under
199697200147
** construction.
199698200148
*/
@@ -199820,21 +200270,42 @@
199820200270
case JSON_FALSE: {
199821200271
jsonAppendRaw(pOut, "false", 5);
199822200272
break;
199823200273
}
199824200274
case JSON_STRING: {
200275
+ assert( pNode->eU==1 );
199825200276
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);
199829200288
}
199830
- /* no break */ deliberate_fall_through
200289
+ break;
199831200290
}
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
+ }
199833200300
case JSON_INT: {
199834200301
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
+ }
199836200307
break;
199837200308
}
199838200309
case JSON_ARRAY: {
199839200310
u32 j = 1;
199840200311
jsonAppendChar(pOut, '[');
@@ -199946,63 +200417,45 @@
199946200417
sqlite3_result_int(pCtx, 0);
199947200418
break;
199948200419
}
199949200420
case JSON_INT: {
199950200421
sqlite3_int64 i = 0;
200422
+ int rc;
200423
+ int bNeg = 0;
199951200424
const char *z;
200425
+
200426
+
199952200427
assert( pNode->eU==1 );
199953200428
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;
199977200440
}
199978200441
case JSON_REAL: {
199979200442
double r;
199980
-#ifdef SQLITE_AMALGAMATION
199981200443
const char *z;
199982200444
assert( pNode->eU==1 );
200445
+ to_double:
199983200446
z = pNode->u.zJContent;
199984200447
sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
199985
-#else
199986
- assert( pNode->eU==1 );
199987
- r = strtod(pNode->u.zJContent, 0);
199988
-#endif
199989200448
sqlite3_result_double(pCtx, r);
199990200449
break;
199991200450
}
199992200451
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() */
199996200452
if( pNode->jnFlags & JNODE_RAW ){
199997200453
assert( pNode->eU==1 );
199998200454
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
199999200455
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 ){
200004200457
/* JSON formatted without any backslash-escapes */
200005200458
assert( pNode->eU==1 );
200006200459
sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
200007200460
SQLITE_TRANSIENT);
200008200461
}else{
@@ -200010,22 +200463,21 @@
200010200463
u32 i;
200011200464
u32 n = pNode->n;
200012200465
const char *z;
200013200466
char *zOut;
200014200467
u32 j;
200468
+ u32 nOut = n;
200015200469
assert( pNode->eU==1 );
200016200470
z = pNode->u.zJContent;
200017
- zOut = sqlite3_malloc( n+1 );
200471
+ zOut = sqlite3_malloc( nOut+1 );
200018200472
if( zOut==0 ){
200019200473
sqlite3_result_error_nomem(pCtx);
200020200474
break;
200021200475
}
200022200476
for(i=1, j=0; i<n-1; i++){
200023200477
char c = z[i];
200024
- if( c!='\\' ){
200025
- zOut[j++] = c;
200026
- }else{
200478
+ if( c=='\\' ){
200027200479
c = z[++i];
200028200480
if( c=='u' ){
200029200481
u32 v = jsonHexToInt4(z+i+1);
200030200482
i += 4;
200031200483
if( v==0 ) break;
@@ -200053,26 +200505,44 @@
200053200505
zOut[j++] = 0xe0 | (v>>12);
200054200506
zOut[j++] = 0x80 | ((v>>6)&0x3f);
200055200507
zOut[j++] = 0x80 | (v&0x3f);
200056200508
}
200057200509
}
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;
200058200538
}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() */
200074200544
zOut[j] = 0;
200075200545
sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
200076200546
}
200077200547
break;
200078200548
}
@@ -200136,28 +200606,159 @@
200136200606
JsonNode *p;
200137200607
if( pParse->aNode==0 || pParse->nNode>=pParse->nAlloc ){
200138200608
return jsonParseAddNodeExpand(pParse, eType, n, zContent);
200139200609
}
200140200610
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);
200143200613
VVA( p->eU = zContent ? 1 : 0 );
200144200614
p->n = n;
200145200615
p->u.zJContent = zContent;
200146200616
return pParse->nNode++;
200147200617
}
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
+}
200148200625
200149200626
/*
200150200627
** Return true if z[] begins with 4 (or more) hexadecimal digits
200151200628
*/
200152200629
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]);
200156200631
}
200157200632
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
+
200159200760
/*
200160200761
** Extra floating-point literals to allow in JSON.
200161200762
*/
200162200763
static const struct NanInfName {
200163200764
char c1;
@@ -200172,204 +200773,455 @@
200172200773
{ 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
200173200774
{ 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },
200174200775
{ 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },
200175200776
{ 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },
200176200777
};
200177
-#endif /* SQLITE_ENABLE_JSON_NAN_INF */
200178200778
200179200779
/*
200180200780
** Parse a single JSON value which begins at pParse->zJson[i]. Return the
200181200781
** index of the first character past the end of the value parsed.
200182200782
**
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
200186200791
*/
200187200792
static int jsonParseValue(JsonParse *pParse, u32 i){
200188200793
char c;
200189200794
u32 j;
200190200795
int iThis;
200191200796
int x;
200192200797
JsonNode *pNode;
200193200798
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 '{': {
200196200802
/* Parse object */
200197200803
iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
200198200804
if( iThis<0 ) return -1;
200805
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
200806
+ pParse->iErr = i;
200807
+ return -1;
200808
+ }
200199200809
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;
200202200811
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
+ }
200207200835
}
200208200836
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
+ }
200211200842
pNode->jnFlags |= JNODE_LABEL;
200212200843
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:
200216200862
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
+ }
200219200867
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;
200225200893
}
200226200894
pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
200895
+ pParse->iDepth--;
200227200896
return j+1;
200228
- }else if( c=='[' ){
200897
+ }
200898
+ case '[': {
200229200899
/* Parse array */
200230200900
iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
200231200901
if( iThis<0 ) return -1;
200902
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
200903
+ pParse->iErr = i;
200904
+ return -1;
200905
+ }
200232200906
memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
200233200907
for(j=i+1;;j++){
200234
- while( fast_isspace(z[j]) ){ j++; }
200235
- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
200236200908
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;
200240200916
return -1;
200241200917
}
200242200918
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;
200248200944
}
200249200945
pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
200946
+ pParse->iDepth--;
200250200947
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 '"':
200252200956
/* Parse string */
200253
- u8 jnFlags = 0;
200957
+ jnFlags = 0;
200958
+ parse_string:
200959
+ cDelim = z[i];
200254200960
j = i+1;
200255200961
for(;;){
200256200962
c = z[j];
200257200963
if( (c & ~0x1f)==0 ){
200258200964
/* Control characters are not allowed in strings */
200965
+ pParse->iErr = j;
200259200966
return -1;
200260200967
}
200261200968
if( c=='\\' ){
200262200969
c = z[++j];
200263200970
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
200264200971
|| 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;
200267200984
}else{
200985
+ pParse->iErr = j;
200268200986
return -1;
200269200987
}
200270
- }else if( c=='"' ){
200988
+ }else if( c==cDelim ){
200271200989
break;
200272200990
}
200273200991
j++;
200274200992
}
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]);
200277200994
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':
200294201038
/* Parse number */
200295
- u8 seenDP = 0;
200296
- u8 seenE = 0;
201039
+ jnFlags = 0;
201040
+ parse_number:
201041
+ seenDP = JSON_INT;
201042
+ seenE = 0;
200297201043
assert( '-' < '0' );
201044
+ assert( '+' < '0' );
201045
+ assert( '.' < '0' );
201046
+ c = z[i];
201047
+
200298201048
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
+ }
200301201096
}
200302
- j = i+1;
200303
- for(;; j++){
201097
+ parse_number_2:
201098
+ for(j=i+1;; j++){
200304201099
c = z[j];
200305
- if( c>='0' && c<='9' ) continue;
201100
+ if( sqlite3Isdigit(c) ) continue;
200306201101
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;
200310201107
continue;
200311201108
}
200312201109
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;
200316201125
c = z[j+1];
200317201126
if( c=='+' || c=='-' ){
200318201127
j++;
200319201128
c = z[j+1];
200320201129
}
200321
- if( c<'0' || c>'9' ) return -1;
201130
+ if( c<'0' || c>'9' ){
201131
+ pParse->iErr = j;
201132
+ return -1;
201133
+ }
200322201134
continue;
200323201135
}
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
200342201136
break;
200343201137
}
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]);
200347201149
return j;
200348
- }else if( c=='}' ){
201150
+ }
201151
+ case '}': {
201152
+ pParse->iErr = i;
200349201153
return -2; /* End of {...} */
200350
- }else if( c==']' ){
201154
+ }
201155
+ case ']': {
201156
+ pParse->iErr = i;
200351201157
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: {
200353201168
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];
200357201207
for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
200358201208
if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
200359201209
nn = aNanInfName[k].n;
200360201210
if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
200361201211
continue;
200362201212
}
200363201213
if( sqlite3Isalnum(z[i+nn]) ) continue;
200364201214
jsonParseAddNode(pParse, aNanInfName[k].eType,
200365201215
aNanInfName[k].nRepl, aNanInfName[k].zRepl);
201216
+ pParse->hasNonstd = 1;
200366201217
return i + nn;
200367201218
}
200368
-#endif
201219
+ pParse->iErr = i;
200369201220
return -1; /* Syntax error */
200370201221
}
201222
+ } /* End switch(z[i]) */
200371201223
}
200372201224
200373201225
/*
200374201226
** Parse a complete JSON string. Return 0 on success or non-zero if there
200375201227
** are any errors. If an error occurs, free all memory associated with
@@ -200389,11 +201241,18 @@
200389201241
i = jsonParseValue(pParse, 0);
200390201242
if( pParse->oom ) i = -1;
200391201243
if( i>0 ){
200392201244
assert( pParse->iDepth==0 );
200393201245
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
+ }
200395201254
}
200396201255
if( i<=0 ){
200397201256
if( pCtx!=0 ){
200398201257
if( pParse->oom ){
200399201258
sqlite3_result_error_nomem(pCtx);
@@ -200460,10 +201319,19 @@
200460201319
** of the argv array. Use the sqlite3_get_auxdata() cache for this
200461201320
** parse if it is available. If the cache is not available or if it
200462201321
** is no longer valid, parse the JSON again and return the new parse,
200463201322
** and also register the new parse so that it will be available for
200464201323
** 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.
200465201333
*/
200466201334
static JsonParse *jsonParseCached(
200467201335
sqlite3_context *pCtx,
200468201336
sqlite3_value **argv,
200469201337
sqlite3_context *pErrCtx
@@ -200509,10 +201377,14 @@
200509201377
}
200510201378
memset(p, 0, sizeof(*p));
200511201379
p->zJson = (char*)&p[1];
200512201380
memcpy((char*)p->zJson, zJson, nJson+1);
200513201381
if( jsonParse(p, pErrCtx, p->zJson) ){
201382
+ if( pErrCtx==0 ){
201383
+ p->nErr = 1;
201384
+ return p;
201385
+ }
200514201386
sqlite3_free(p);
200515201387
return 0;
200516201388
}
200517201389
p->nJson = nJson;
200518201390
p->iHold = iMaxHold+1;
@@ -200523,19 +201395,28 @@
200523201395
200524201396
/*
200525201397
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
200526201398
** a match.
200527201399
*/
200528
-static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
201400
+static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
200529201401
assert( pNode->eU==1 );
200530201402
if( pNode->jnFlags & JNODE_RAW ){
200531201403
if( pNode->n!=nKey ) return 0;
200532201404
return strncmp(pNode->u.zJContent, zKey, nKey)==0;
200533201405
}else{
200534201406
if( pNode->n!=nKey+2 ) return 0;
200535201407
return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
200536201408
}
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
+ }
200537201418
}
200538201419
200539201420
/* forward declaration */
200540201421
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
200541201422
@@ -201003,11 +201884,11 @@
201003201884
if( argc==2 ){
201004201885
/* With a single PATH argument */
201005201886
zPath = (const char*)sqlite3_value_text(argv[1]);
201006201887
if( zPath==0 ) return;
201007201888
if( flags & JSON_ABPATH ){
201008
- if( zPath[0]!='$' ){
201889
+ if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
201009201890
/* The -> and ->> operators accept abbreviated PATH arguments. This
201010201891
** is mostly for compatibility with PostgreSQL, but also for
201011201892
** convenience.
201012201893
**
201013201894
** NUMBER ==> $[NUMBER] // PG compatible
@@ -201094,16 +201975,14 @@
201094201975
assert( pPatch[i].eType==JSON_STRING );
201095201976
assert( pPatch[i].jnFlags & JNODE_LABEL );
201096201977
assert( pPatch[i].eU==1 );
201097201978
nKey = pPatch[i].n;
201098201979
zKey = pPatch[i].u.zJContent;
201099
- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
201100201980
for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
201101201981
assert( pTarget[j].eType==JSON_STRING );
201102201982
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]) ){
201105201984
if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
201106201985
if( pPatch[i+1].eType==JSON_NULL ){
201107201986
pTarget[j+1].jnFlags |= JNODE_REMOVE;
201108201987
}else{
201109201988
JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
@@ -201386,22 +202265,83 @@
201386202265
}
201387202266
201388202267
/*
201389202268
** json_valid(JSON)
201390202269
**
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.
201393202272
*/
201394202273
static void jsonValidFunc(
201395202274
sqlite3_context *ctx,
201396202275
int argc,
201397202276
sqlite3_value **argv
201398202277
){
201399202278
JsonParse *p; /* The parse */
201400202279
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;
201401202327
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
+ }
201403202343
}
201404202344
201405202345
201406202346
/****************************************************************************
201407202347
** Aggregate SQL function implementations
@@ -201741,18 +202681,20 @@
201741202681
assert( pNode->eType==JSON_STRING );
201742202682
assert( pNode->jnFlags & JNODE_LABEL );
201743202683
assert( pNode->eU==1 );
201744202684
z = pNode->u.zJContent;
201745202685
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
+ }
201754202696
}
201755202697
}
201756202698
jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
201757202699
}
201758202700
@@ -202108,10 +203050,11 @@
202108203050
static FuncDef aJsonFunc[] = {
202109203051
JFUNCTION(json, 1, 0, jsonRemoveFunc),
202110203052
JFUNCTION(json_array, -1, 0, jsonArrayFunc),
202111203053
JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
202112203054
JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
203055
+ JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
202113203056
JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
202114203057
JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
202115203058
JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
202116203059
JFUNCTION(json_insert, -1, 0, jsonSetFunc),
202117203060
JFUNCTION(json_object, -1, 0, jsonObjectFunc),
@@ -202632,20 +203575,21 @@
202632203575
** using C-preprocessor macros. If that is unsuccessful, or if
202633203576
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
202634203577
** at run-time.
202635203578
*/
202636203579
#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
202647203591
#endif
202648203592
202649203593
202650203594
/* What version of MSVC is being used. 0 means MSVC is not being used */
202651203595
#ifndef MSVC_VERSION
@@ -213201,15 +214145,22 @@
213201214145
static int rbuLockDatabase(sqlite3 *db){
213202214146
int rc = SQLITE_OK;
213203214147
sqlite3_file *fd = 0;
213204214148
213205214149
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{
213207214158
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
213208214159
}
213209214160
213210
- if( fd->pMethods ){
214161
+ if( rc==SQLITE_OK && fd->pMethods ){
213211214162
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
213212214163
if( rc==SQLITE_OK ){
213213214164
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE);
213214214165
}
213215214166
}
@@ -216825,10 +217776,12 @@
216825217776
# define SESSIONS_STRM_CHUNK_SIZE 64
216826217777
# else
216827217778
# define SESSIONS_STRM_CHUNK_SIZE 1024
216828217779
# endif
216829217780
#endif
217781
+
217782
+#define SESSIONS_ROWID "_rowid_"
216830217783
216831217784
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
216832217785
216833217786
typedef struct SessionHook SessionHook;
216834217787
struct SessionHook {
@@ -216847,10 +217800,11 @@
216847217800
char *zDb; /* Name of database session is attached to */
216848217801
int bEnableSize; /* True if changeset_size() enabled */
216849217802
int bEnable; /* True if currently recording */
216850217803
int bIndirect; /* True if all changes are indirect */
216851217804
int bAutoAttach; /* True to auto-attach tables */
217805
+ int bImplicitPK; /* True to handle tables with implicit PK */
216852217806
int rc; /* Non-zero if an error has occurred */
216853217807
void *pFilterCtx; /* First argument to pass to xTableFilter */
216854217808
int (*xTableFilter)(void *pCtx, const char *zTab);
216855217809
i64 nMalloc; /* Number of bytes of data allocated */
216856217810
i64 nMaxChangesetSize;
@@ -216923,10 +217877,11 @@
216923217877
struct SessionTable {
216924217878
SessionTable *pNext;
216925217879
char *zName; /* Local name of table */
216926217880
int nCol; /* Number of columns in table zName */
216927217881
int bStat1; /* True if this is sqlite_stat1 */
217882
+ int bRowid; /* True if this table uses rowid for PK */
216928217883
const char **azCol; /* Column names */
216929217884
u8 *abPK; /* Array of primary key flags */
216930217885
int nEntry; /* Total number of entries in hash table */
216931217886
int nChange; /* Size of apChange[] array */
216932217887
SessionChange **apChange; /* Hash table buckets */
@@ -217315,60 +218270,66 @@
217315218270
** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned
217316218271
** and the output variables are set as described above.
217317218272
*/
217318218273
static int sessionPreupdateHash(
217319218274
sqlite3_session *pSession, /* Session object that owns pTab */
218275
+ i64 iRowid,
217320218276
SessionTable *pTab, /* Session table handle */
217321218277
int bNew, /* True to hash the new.* PK */
217322218278
int *piHash, /* OUT: Hash value */
217323218279
int *pbNullPK /* OUT: True if there are NULL values in PK */
217324218280
){
217325218281
unsigned int h = 0; /* Hash value to return */
217326218282
int i; /* Used to iterate through columns */
217327218283
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
+ }
217370218331
}
217371218332
}
217372218333
}
217373218334
217374218335
*piHash = (h % pTab->nChange);
@@ -217647,16 +218608,22 @@
217647218608
** if the pre-update-hook does not affect the same row as pChange, it returns
217648218609
** false.
217649218610
*/
217650218611
static int sessionPreupdateEqual(
217651218612
sqlite3_session *pSession, /* Session object that owns SessionTable */
218613
+ i64 iRowid, /* Rowid value if pTab->bRowid */
217652218614
SessionTable *pTab, /* Table associated with change */
217653218615
SessionChange *pChange, /* Change to compare to */
217654218616
int op /* Current pre-update operation */
217655218617
){
217656218618
int iCol; /* Used to iterate through columns */
217657218619
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
+ }
217658218625
217659218626
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
217660218627
for(iCol=0; iCol<pTab->nCol; iCol++){
217661218628
if( !pTab->abPK[iCol] ){
217662218629
a += sessionSerialLen(a);
@@ -217798,11 +218765,12 @@
217798218765
const char *zDb, /* Name of attached database (e.g. "main") */
217799218766
const char *zThis, /* Table name */
217800218767
int *pnCol, /* OUT: number of columns */
217801218768
const char **pzTab, /* OUT: Copy of zThis */
217802218769
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 */
217804218772
){
217805218773
char *zPragma;
217806218774
sqlite3_stmt *pStmt;
217807218775
int rc;
217808218776
sqlite3_int64 nByte;
@@ -217810,10 +218778,11 @@
217810218778
int nThis;
217811218779
int i;
217812218780
u8 *pAlloc = 0;
217813218781
char **azCol = 0;
217814218782
u8 *abPK = 0;
218783
+ int bRowid = 0; /* Set to true to use rowid as PK */
217815218784
217816218785
assert( pazCol && pabPK );
217817218786
217818218787
nThis = sqlite3Strlen30(zThis);
217819218788
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
@@ -217854,14 +218823,19 @@
217854218823
if( pzTab ) *pzTab = 0;
217855218824
return rc;
217856218825
}
217857218826
217858218827
nByte = nThis + 1;
218828
+ bRowid = (pbRowid!=0);
217859218829
while( SQLITE_ROW==sqlite3_step(pStmt) ){
217860218830
nByte += sqlite3_column_bytes(pStmt, 1);
217861218831
nDbCol++;
218832
+ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
217862218833
}
218834
+ if( nDbCol==0 ) bRowid = 0;
218835
+ nDbCol += bRowid;
218836
+ nByte += strlen(SESSIONS_ROWID);
217863218837
rc = sqlite3_reset(pStmt);
217864218838
217865218839
if( rc==SQLITE_OK ){
217866218840
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
217867218841
pAlloc = sessionMalloc64(pSession, nByte);
@@ -217879,10 +218853,18 @@
217879218853
*pzTab = (char *)pAlloc;
217880218854
pAlloc += nThis+1;
217881218855
}
217882218856
217883218857
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
+ }
217884218866
while( SQLITE_ROW==sqlite3_step(pStmt) ){
217885218867
int nName = sqlite3_column_bytes(pStmt, 1);
217886218868
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
217887218869
if( zName==0 ) break;
217888218870
memcpy(pAlloc, zName, nName+1);
@@ -217890,11 +218872,10 @@
217890218872
pAlloc += nName+1;
217891218873
abPK[i] = sqlite3_column_int(pStmt, 5);
217892218874
i++;
217893218875
}
217894218876
rc = sqlite3_reset(pStmt);
217895
-
217896218877
}
217897218878
217898218879
/* If successful, populate the output variables. Otherwise, zero them and
217899218880
** free any allocation made. An error code will be returned in this case.
217900218881
*/
@@ -217907,10 +218888,11 @@
217907218888
*pabPK = 0;
217908218889
*pnCol = 0;
217909218890
if( pzTab ) *pzTab = 0;
217910218891
sessionFree(pSession, azCol);
217911218892
}
218893
+ if( pbRowid ) *pbRowid = bRowid;
217912218894
sqlite3_finalize(pStmt);
217913218895
return rc;
217914218896
}
217915218897
217916218898
/*
@@ -217928,11 +218910,12 @@
217928218910
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
217929218911
if( pTab->nCol==0 ){
217930218912
u8 *abPK;
217931218913
assert( pTab->azCol==0 || pTab->abPK==0 );
217932218914
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)
217934218917
);
217935218918
if( pSession->rc==SQLITE_OK ){
217936218919
int i;
217937218920
for(i=0; i<pTab->nCol; i++){
217938218921
if( abPK[i] ){
@@ -218000,10 +218983,11 @@
218000218983
SessionTable *pTab, /* Table that change applies to */
218001218984
SessionChange *pC /* Update pC->nMaxSize */
218002218985
){
218003218986
i64 nNew = 2;
218004218987
if( pC->op==SQLITE_INSERT ){
218988
+ if( pTab->bRowid ) nNew += 9;
218005218989
if( op!=SQLITE_DELETE ){
218006218990
int ii;
218007218991
for(ii=0; ii<pTab->nCol; ii++){
218008218992
sqlite3_value *p = 0;
218009218993
pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
@@ -218016,16 +219000,20 @@
218016219000
nNew += pC->nRecord;
218017219001
}
218018219002
}else{
218019219003
int ii;
218020219004
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++){
218022219010
int bChanged = 1;
218023219011
int nOld = 0;
218024219012
int eType;
218025219013
sqlite3_value *p = 0;
218026
- pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
219014
+ pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p);
218027219015
if( p==0 ){
218028219016
return SQLITE_NOMEM;
218029219017
}
218030219018
218031219019
eType = *pCsr++;
@@ -218100,10 +219088,11 @@
218100219088
** Unless one is already present or an error occurs, an entry is added
218101219089
** to the changed-rows hash table associated with table pTab.
218102219090
*/
218103219091
static void sessionPreupdateOneChange(
218104219092
int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
219093
+ i64 iRowid,
218105219094
sqlite3_session *pSession, /* Session object pTab is attached to */
218106219095
SessionTable *pTab /* Table that change applies to */
218107219096
){
218108219097
int iHash;
218109219098
int bNull = 0;
@@ -218115,11 +219104,11 @@
218115219104
/* Load table details if required */
218116219105
if( sessionInitTable(pSession, pTab) ) return;
218117219106
218118219107
/* Check the number of columns in this xPreUpdate call matches the
218119219108
** 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) ){
218121219110
pSession->rc = SQLITE_SCHEMA;
218122219111
return;
218123219112
}
218124219113
218125219114
/* Grow the hash table if required */
@@ -218148,18 +219137,20 @@
218148219137
}
218149219138
218150219139
/* Calculate the hash-key for this change. If the primary key of the row
218151219140
** includes a NULL value, exit early. Such changes are ignored by the
218152219141
** 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
+ );
218154219145
if( rc!=SQLITE_OK ) goto error_out;
218155219146
218156219147
if( bNull==0 ){
218157219148
/* Search the hash table for an existing record for this row. */
218158219149
SessionChange *pC;
218159219150
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;
218161219152
}
218162219153
218163219154
if( pC==0 ){
218164219155
/* Create a new change object containing all the old values (if
218165219156
** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
@@ -218170,11 +219161,11 @@
218170219161
assert( rc==SQLITE_OK );
218171219162
pTab->nEntry++;
218172219163
218173219164
/* Figure out how large an allocation is required */
218174219165
nByte = sizeof(SessionChange);
218175
- for(i=0; i<pTab->nCol; i++){
219166
+ for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
218176219167
sqlite3_value *p = 0;
218177219168
if( op!=SQLITE_INSERT ){
218178219169
TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
218179219170
assert( trc==SQLITE_OK );
218180219171
}else if( pTab->abPK[i] ){
@@ -218185,10 +219176,13 @@
218185219176
/* This may fail if SQLite value p contains a utf-16 string that must
218186219177
** be converted to utf-8 and an OOM error occurs while doing so. */
218187219178
rc = sessionSerializeValue(0, p, &nByte);
218188219179
if( rc!=SQLITE_OK ) goto error_out;
218189219180
}
219181
+ if( pTab->bRowid ){
219182
+ nByte += 9; /* Size of rowid field - an integer */
219183
+ }
218190219184
218191219185
/* Allocate the change object */
218192219186
pC = (SessionChange *)sessionMalloc64(pSession, nByte);
218193219187
if( !pC ){
218194219188
rc = SQLITE_NOMEM;
@@ -218201,11 +219195,16 @@
218201219195
/* Populate the change object. None of the preupdate_old(),
218202219196
** preupdate_new() or SerializeValue() calls below may fail as all
218203219197
** required values and encodings have already been cached in memory.
218204219198
** It is not possible for an OOM to occur in this block. */
218205219199
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++){
218207219206
sqlite3_value *p = 0;
218208219207
if( op!=SQLITE_INSERT ){
218209219208
pSession->hook.xOld(pSession->hook.pCtx, i, &p);
218210219209
}else if( pTab->abPK[i] ){
218211219210
pSession->hook.xNew(pSession->hook.pCtx, i, &p);
@@ -218316,13 +219315,14 @@
218316219315
if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
218317219316
218318219317
pSession->rc = sessionFindTable(pSession, zName, &pTab);
218319219318
if( pTab ){
218320219319
assert( pSession->rc==SQLITE_OK );
218321
- sessionPreupdateOneChange(op, pSession, pTab);
219320
+ assert( op==SQLITE_UPDATE || iKey1==iKey2 );
219321
+ sessionPreupdateOneChange(op, iKey1, pSession, pTab);
218322219322
if( op==SQLITE_UPDATE ){
218323
- sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
219323
+ sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab);
218324219324
}
218325219325
}
218326219326
}
218327219327
}
218328219328
@@ -218357,29 +219357,30 @@
218357219357
}
218358219358
218359219359
typedef struct SessionDiffCtx SessionDiffCtx;
218360219360
struct SessionDiffCtx {
218361219361
sqlite3_stmt *pStmt;
219362
+ int bRowid;
218362219363
int nOldOff;
218363219364
};
218364219365
218365219366
/*
218366219367
** The diff hook implementations.
218367219368
*/
218368219369
static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
218369219370
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);
218371219372
return SQLITE_OK;
218372219373
}
218373219374
static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
218374219375
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
218375
- *ppVal = sqlite3_column_value(p->pStmt, iVal);
219376
+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid);
218376219377
return SQLITE_OK;
218377219378
}
218378219379
static int sessionDiffCount(void *pCtx){
218379219380
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;
218381219382
}
218382219383
static int sessionDiffDepth(void *pCtx){
218383219384
(void)pCtx;
218384219385
return 0;
218385219386
}
@@ -218454,18 +219455,20 @@
218454219455
}
218455219456
218456219457
static char *sessionSelectFindNew(
218457219458
const char *zDb1, /* Pick rows in this db only */
218458219459
const char *zDb2, /* But not in this one */
219460
+ int bRowid,
218459219461
const char *zTbl, /* Table name */
218460219462
const char *zExpr
218461219463
){
219464
+ const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*");
218462219465
char *zRet = sqlite3_mprintf(
218463
- "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
219466
+ "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
218464219467
" SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
218465219468
")",
218466
- zDb1, zTbl, zDb2, zTbl, zExpr
219469
+ zSel, zDb1, zTbl, zDb2, zTbl, zExpr
218467219470
);
218468219471
return zRet;
218469219472
}
218470219473
218471219474
static int sessionDiffFindNew(
@@ -218475,11 +219478,13 @@
218475219478
const char *zDb1,
218476219479
const char *zDb2,
218477219480
char *zExpr
218478219481
){
218479219482
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
+ );
218481219486
218482219487
if( zStmt==0 ){
218483219488
rc = SQLITE_NOMEM;
218484219489
}else{
218485219490
sqlite3_stmt *pStmt;
@@ -218486,20 +219491,43 @@
218486219491
rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
218487219492
if( rc==SQLITE_OK ){
218488219493
SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
218489219494
pDiffCtx->pStmt = pStmt;
218490219495
pDiffCtx->nOldOff = 0;
219496
+ pDiffCtx->bRowid = pTab->bRowid;
218491219497
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);
218493219500
}
218494219501
rc = sqlite3_finalize(pStmt);
218495219502
}
218496219503
sqlite3_free(zStmt);
218497219504
}
218498219505
218499219506
return rc;
218500219507
}
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
+}
218501219529
218502219530
static int sessionDiffFindModified(
218503219531
sqlite3_session *pSession,
218504219532
SessionTable *pTab,
218505219533
const char *zFrom,
@@ -218511,15 +219539,17 @@
218511219539
pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK
218512219540
);
218513219541
if( zExpr2==0 ){
218514219542
rc = SQLITE_NOMEM;
218515219543
}else{
219544
+ char *z1 = sessionAllCols(pSession->zDb, pTab);
219545
+ char *z2 = sessionAllCols(zFrom, pTab);
218516219546
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
218519219549
);
218520
- if( zStmt==0 ){
219550
+ if( zStmt==0 || z1==0 || z2==0 ){
218521219551
rc = SQLITE_NOMEM;
218522219552
}else{
218523219553
sqlite3_stmt *pStmt;
218524219554
rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
218525219555
@@ -218526,16 +219556,19 @@
218526219556
if( rc==SQLITE_OK ){
218527219557
SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
218528219558
pDiffCtx->pStmt = pStmt;
218529219559
pDiffCtx->nOldOff = pTab->nCol;
218530219560
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);
218532219563
}
218533219564
rc = sqlite3_finalize(pStmt);
218534219565
}
218535
- sqlite3_free(zStmt);
218536219566
}
219567
+ sqlite3_free(zStmt);
219568
+ sqlite3_free(z1);
219569
+ sqlite3_free(z2);
218537219570
}
218538219571
218539219572
return rc;
218540219573
}
218541219574
@@ -218570,13 +219603,16 @@
218570219603
/* Check the table schemas match */
218571219604
if( rc==SQLITE_OK ){
218572219605
int bHasPk = 0;
218573219606
int bMismatch = 0;
218574219607
int nCol; /* Columns in zFrom.zTbl */
219608
+ int bRowid = 0;
218575219609
u8 *abPK;
218576219610
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
+ );
218578219614
if( rc==SQLITE_OK ){
218579219615
if( pTo->nCol!=nCol ){
218580219616
bMismatch = 1;
218581219617
}else{
218582219618
int i;
@@ -219121,11 +220157,11 @@
219121220157
}
219122220158
219123220159
/* If at least one field has been modified, this is not a no-op. */
219124220160
if( bChanged ) bNoop = 0;
219125220161
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
219127220163
** currently generating a patchset. */
219128220164
if( bPatchset==0 ){
219129220165
if( bChanged || abPK[i] ){
219130220166
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
219131220167
}else{
@@ -219223,19 +220259,20 @@
219223220259
static int sessionSelectStmt(
219224220260
sqlite3 *db, /* Database handle */
219225220261
int bIgnoreNoop,
219226220262
const char *zDb, /* Database name */
219227220263
const char *zTab, /* Table name */
220264
+ int bRowid,
219228220265
int nCol, /* Number of columns in table */
219229220266
const char **azCol, /* Names of table columns */
219230220267
u8 *abPK, /* PRIMARY KEY array */
219231220268
sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
219232220269
){
219233220270
int rc = SQLITE_OK;
219234220271
char *zSql = 0;
219235220272
const char *zSep = "";
219236
- const char *zCols = "*";
220273
+ const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*";
219237220274
int nSql = -1;
219238220275
int i;
219239220276
219240220277
SessionBuffer nooptest = {0, 0, 0};
219241220278
SessionBuffer pkfield = {0, 0, 0};
@@ -219250,11 +220287,10 @@
219250220287
"?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc
219251220288
);
219252220289
zCols = "tbl, ?2, stat";
219253220290
}else{
219254220291
for(i=0; i<nCol; i++){
219255
-
219256220292
if( abPK[i] ){
219257220293
sessionAppendStr(&pkfield, zSep, &rc);
219258220294
sessionAppendStr(&pkvar, zSep, &rc);
219259220295
zSep = ", ";
219260220296
sessionAppendIdent(&pkfield, azCol[i], &rc);
@@ -219457,24 +220493,32 @@
219457220493
const char **azCol = 0; /* Table columns */
219458220494
int i; /* Used to iterate through hash buckets */
219459220495
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
219460220496
int nRewind = buf.nBuf; /* Initial size of write buffer */
219461220497
int nNoop; /* Size of buffer after writing tbl header */
220498
+ int bRowid = 0;
219462220499
219463220500
/* 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
+ )){
219466220510
rc = SQLITE_SCHEMA;
219467220511
}
219468220512
219469220513
/* Write a table header */
219470220514
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
219471220515
219472220516
/* Build and compile a statement to execute: */
219473220517
if( rc==SQLITE_OK ){
219474220518
rc = sessionSelectStmt(
219475
- db, 0, pSession->zDb, zName, nCol, azCol, abPK, &pSel
220519
+ db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
219476220520
);
219477220521
}
219478220522
219479220523
nNoop = buf.nBuf;
219480220524
for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
@@ -219554,11 +220598,11 @@
219554220598
void **ppChangeset /* OUT: Buffer containing changeset */
219555220599
){
219556220600
int rc;
219557220601
219558220602
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);
219560220604
assert( rc || pnChangeset==0
219561220605
|| pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
219562220606
);
219563220607
return rc;
219564220608
}
@@ -219671,10 +220715,23 @@
219671220715
}
219672220716
}
219673220717
*(int*)pArg = pSession->bEnableSize;
219674220718
break;
219675220719
}
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
+ }
219676220733
219677220734
default:
219678220735
rc = SQLITE_MISUSE;
219679220736
}
219680220737
@@ -220661,10 +221718,11 @@
220661221718
SessionBuffer constraints; /* Deferred constraints are stored here */
220662221719
SessionBuffer rebase; /* Rebase information (if any) here */
220663221720
u8 bRebaseStarted; /* If table header is already in rebase */
220664221721
u8 bRebase; /* True to collect rebase information */
220665221722
u8 bIgnoreNoop; /* True to ignore no-op conflicts */
221723
+ int bRowid;
220666221724
};
220667221725
220668221726
/* Number of prepared UPDATE statements to cache. */
220669221727
#define SESSION_UPDATE_CACHE_SZ 12
220670221728
@@ -220911,12 +221969,13 @@
220911221969
static int sessionSelectRow(
220912221970
sqlite3 *db, /* Database handle */
220913221971
const char *zTab, /* Table name */
220914221972
SessionApplyCtx *p /* Session changeset-apply context */
220915221973
){
221974
+ /* TODO */
220916221975
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
220918221977
);
220919221978
}
220920221979
220921221980
/*
220922221981
** Formulate and prepare an INSERT statement to add a record to table zTab.
@@ -221608,10 +222667,11 @@
221608222667
sApply.azCol = 0;
221609222668
sApply.abPK = 0;
221610222669
sApply.bStat1 = 0;
221611222670
sApply.bDeferConstraints = 1;
221612222671
sApply.bRebaseStarted = 0;
222672
+ sApply.bRowid = 0;
221613222673
memset(&sApply.constraints, 0, sizeof(SessionBuffer));
221614222674
221615222675
/* If an xFilter() callback was specified, invoke it now. If the
221616222676
** xFilter callback returns zero, skip this table. If it returns
221617222677
** non-zero, proceed. */
@@ -221627,12 +222687,12 @@
221627222687
}else{
221628222688
int nMinCol = 0;
221629222689
int i;
221630222690
221631222691
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
221634222694
);
221635222695
if( rc!=SQLITE_OK ) break;
221636222696
for(i=0; i<sApply.nCol; i++){
221637222697
if( sApply.abPK[i] ) nMinCol = i+1;
221638222698
}
@@ -223510,29 +224570,34 @@
223510224570
fts5_tokenizer *pTokApi;
223511224571
int bLock; /* True when table is preparing statement */
223512224572
int ePattern; /* FTS_PATTERN_XXX constant */
223513224573
223514224574
/* Values loaded from the %_config table */
224575
+ int iVersion; /* fts5 file format 'version' */
223515224576
int iCookie; /* Incremented when %_config is modified */
223516224577
int pgsz; /* Approximate page size used in %_data */
223517224578
int nAutomerge; /* 'automerge' setting */
223518224579
int nCrisisMerge; /* Maximum allowed segments per level */
223519224580
int nUsermerge; /* 'usermerge' setting */
223520224581
int nHashSize; /* Bytes of memory for in-memory hash */
223521224582
char *zRank; /* Name of rank function */
223522224583
char *zRankArgs; /* Arguments to rank function */
224584
+ int bSecureDelete; /* 'secure-delete' */
223523224585
223524224586
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
223525224587
char **pzErrmsg;
223526224588
223527224589
#ifdef SQLITE_DEBUG
223528224590
int bPrefixIndex; /* True to use prefix-indexes */
223529224591
#endif
223530224592
};
223531224593
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
223534224599
223535224600
#define FTS5_CONTENT_NORMAL 0
223536224601
#define FTS5_CONTENT_NONE 1
223537224602
#define FTS5_CONTENT_EXTERNAL 2
223538224603
@@ -223694,10 +224759,11 @@
223694224759
/* The following are used internally by the fts5_index.c module. They are
223695224760
** defined here only to make it easier to avoid clashes with the flags
223696224761
** above. */
223697224762
#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
223698224763
#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
224764
+#define FTS5INDEX_QUERY_SKIPHASH 0x0040
223699224765
223700224766
/*
223701224767
** Create/destroy an Fts5Index object.
223702224768
*/
223703224769
static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**);
@@ -223848,11 +224914,11 @@
223848224914
static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v);
223849224915
static int sqlite3Fts5GetVarintLen(u32 iVal);
223850224916
static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*);
223851224917
static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
223852224918
223853
-#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b)
224919
+#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b))
223854224920
#define fts5GetVarint sqlite3Fts5GetVarint
223855224921
223856224922
#define fts5FastGetVarint32(a, iOff, nVal) { \
223857224923
nVal = (a)[iOff++]; \
223858224924
if( nVal & 0x80 ){ \
@@ -227340,10 +228406,11 @@
227340228406
if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
227341228407
*pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
227342228408
rc = SQLITE_ERROR;
227343228409
}
227344228410
228411
+ assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK );
227345228412
for(i=3; rc==SQLITE_OK && i<nArg; i++){
227346228413
const char *zOrig = azArg[i];
227347228414
const char *z;
227348228415
char *zOne = 0;
227349228416
char *zTwo = 0;
@@ -227693,10 +228760,22 @@
227693228760
pConfig->zRankArgs = zRankArgs;
227694228761
}else if( rc==SQLITE_ERROR ){
227695228762
rc = SQLITE_OK;
227696228763
*pbBadkey = 1;
227697228764
}
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
+ }
227698228777
}else{
227699228778
*pbBadkey = 1;
227700228779
}
227701228780
return rc;
227702228781
}
@@ -227737,19 +228816,24 @@
227737228816
}
227738228817
}
227739228818
rc = sqlite3_finalize(p);
227740228819
}
227741228820
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
+ ){
227743228825
rc = SQLITE_ERROR;
227744228826
if( pConfig->pzErrmsg ){
227745228827
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
227749228831
);
227750228832
}
228833
+ }else{
228834
+ pConfig->iVersion = iVersion;
227751228835
}
227752228836
227753228837
if( rc==SQLITE_OK ){
227754228838
pConfig->iCookie = iCookie;
227755228839
}
@@ -227772,10 +228856,14 @@
227772228856
227773228857
227774228858
227775228859
/* #include "fts5Int.h" */
227776228860
/* #include "fts5parse.h" */
228861
+
228862
+#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH
228863
+# define SQLITE_FTS5_MAX_EXPR_DEPTH 256
228864
+#endif
227777228865
227778228866
/*
227779228867
** All token types in the generated fts5parse.h file are greater than 0.
227780228868
*/
227781228869
#define FTS5_EOF 0
@@ -227813,15 +228901,21 @@
227813228901
** FTS5_AND (nChild, apChild valid)
227814228902
** FTS5_OR (nChild, apChild valid)
227815228903
** FTS5_NOT (nChild, apChild valid)
227816228904
** FTS5_STRING (pNear valid)
227817228905
** 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.
227818228911
*/
227819228912
struct Fts5ExprNode {
227820228913
int eType; /* Node type */
227821228914
int bEof; /* True at EOF */
227822228915
int bNomatch; /* True if entry is not a match */
228916
+ int iHeight; /* Distance to tree leaf nodes */
227823228917
227824228918
/* Next method for this node. */
227825228919
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
227826228920
227827228921
i64 iRowid; /* Current rowid */
@@ -227887,10 +228981,35 @@
227887228981
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
227888228982
Fts5ExprNode *pExpr; /* Result of a successful parse */
227889228983
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
227890228984
};
227891228985
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
+
227892229011
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
227893229012
va_list ap;
227894229013
va_start(ap, zFmt);
227895229014
if( pParse->rc==SQLITE_OK ){
227896229015
assert( pParse->zErr==0 );
@@ -228000,10 +229119,12 @@
228000229119
do {
228001229120
t = fts5ExprGetToken(&sParse, &z, &token);
228002229121
sqlite3Fts5Parser(pEngine, t, token, &sParse);
228003229122
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
228004229123
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
229124
+
229125
+ assert_expr_depth_ok(sParse.rc, sParse.pExpr);
228005229126
228006229127
/* If the LHS of the MATCH expression was a user column, apply the
228007229128
** implicit column-filter. */
228008229129
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
228009229130
int n = sizeof(Fts5Colset);
@@ -228163,11 +229284,11 @@
228163229284
228164229285
static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
228165229286
Fts5Parse sParse;
228166229287
memset(&sParse, 0, sizeof(sParse));
228167229288
228168
- if( *pp1 ){
229289
+ if( *pp1 && p2 ){
228169229290
Fts5Expr *p1 = *pp1;
228170229291
int nPhrase = p1->nPhrase + p2->nPhrase;
228171229292
228172229293
p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
228173229294
p2->pRoot = 0;
@@ -228188,11 +229309,11 @@
228188229309
p1->apExprPhrase = ap;
228189229310
}
228190229311
}
228191229312
sqlite3_free(p2->apExprPhrase);
228192229313
sqlite3_free(p2);
228193
- }else{
229314
+ }else if( p2 ){
228194229315
*pp1 = p2;
228195229316
}
228196229317
228197229318
return sParse.rc;
228198229319
}
@@ -229962,18 +231083,22 @@
229962231083
};
229963231084
}
229964231085
}
229965231086
229966231087
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
231088
+ int ii = p->nChild;
229967231089
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
229968231090
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
229969231091
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
229970231092
p->nChild += pSub->nChild;
229971231093
sqlite3_free(pSub);
229972231094
}else{
229973231095
p->apChild[p->nChild++] = pSub;
229974231096
}
231097
+ for( ; ii<p->nChild; ii++){
231098
+ p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1);
231099
+ }
229975231100
}
229976231101
229977231102
/*
229978231103
** This function is used when parsing LIKE or GLOB patterns against
229979231104
** trigram indexes that specify either detail=column or detail=none.
@@ -230000,10 +231125,11 @@
230000231125
nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
230001231126
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
230002231127
if( pRet ){
230003231128
pRet->eType = FTS5_AND;
230004231129
pRet->nChild = nTerm;
231130
+ pRet->iHeight = 1;
230005231131
fts5ExprAssignXNext(pRet);
230006231132
pParse->nPhrase--;
230007231133
for(ii=0; ii<nTerm; ii++){
230008231134
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
230009231135
&pParse->rc, sizeof(Fts5ExprPhrase)
@@ -230105,10 +231231,18 @@
230105231231
}
230106231232
}
230107231233
}else{
230108231234
fts5ExprAddChildren(pRet, pLeft);
230109231235
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
+ }
230110231244
}
230111231245
}
230112231246
}
230113231247
}
230114231248
@@ -231705,10 +232839,12 @@
231705232839
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
231706232840
sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
231707232841
sqlite3_stmt *pIdxSelect;
231708232842
int nRead; /* Total number of blocks read */
231709232843
232844
+ sqlite3_stmt *pDeleteFromIdx;
232845
+
231710232846
sqlite3_stmt *pDataVersion;
231711232847
i64 iStructVersion; /* data_version when pStruct read */
231712232848
Fts5Structure *pStruct; /* Current db structure (or NULL) */
231713232849
};
231714232850
@@ -231797,13 +232933,10 @@
231797232933
** Current leaf page number within segment.
231798232934
**
231799232935
** iLeafOffset:
231800232936
** Byte offset within the current leaf that is the first byte of the
231801232937
** 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.
231805232938
**
231806232939
** pLeaf:
231807232940
** Buffer containing current leaf page data. Set to NULL at EOF.
231808232941
**
231809232942
** iTermLeafPgno, iTermLeafOffset:
@@ -232358,10 +233491,11 @@
232358233491
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
232359233492
if( i>=nData ){
232360233493
rc = FTS5_CORRUPT;
232361233494
break;
232362233495
}
233496
+ assert( pSeg!=0 );
232363233497
i += fts5GetVarint32(&pData[i], pSeg->iSegid);
232364233498
i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst);
232365233499
i += fts5GetVarint32(&pData[i], pSeg->pgnoLast);
232366233500
if( pSeg->pgnoLast<pSeg->pgnoFirst ){
232367233501
rc = FTS5_CORRUPT;
@@ -232388,10 +233522,11 @@
232388233522
** Add a level to the Fts5Structure.aLevel[] array of structure object
232389233523
** (*ppStruct).
232390233524
*/
232391233525
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
232392233526
fts5StructureMakeWritable(pRc, ppStruct);
233527
+ assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
232393233528
if( *pRc==SQLITE_OK ){
232394233529
Fts5Structure *pStruct = *ppStruct;
232395233530
int nLevel = pStruct->nLevel;
232396233531
sqlite3_int64 nByte = (
232397233532
sizeof(Fts5Structure) + /* Main structure */
@@ -232846,46 +233981,29 @@
232846233981
assert( pLvl->bEof==0 );
232847233982
if( iOff<=pLvl->iFirstOff ){
232848233983
pLvl->bEof = 1;
232849233984
}else{
232850233985
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
+ }
232887234005
}
232888234006
232889234007
return pLvl->bEof;
232890234008
}
232891234009
@@ -233077,11 +234195,11 @@
233077234195
static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
233078234196
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
233079234197
i64 iOff = pIter->iLeafOffset;
233080234198
233081234199
ASSERT_SZLEAF_OK(pIter->pLeaf);
233082
- if( iOff>=pIter->pLeaf->szLeaf ){
234200
+ while( iOff>=pIter->pLeaf->szLeaf ){
233083234201
fts5SegIterNextPage(p, pIter);
233084234202
if( pIter->pLeaf==0 ){
233085234203
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
233086234204
return;
233087234205
}
@@ -233176,14 +234294,16 @@
233176234294
if( p->rc==SQLITE_OK ){
233177234295
memset(pIter, 0, sizeof(*pIter));
233178234296
fts5SegIterSetNext(p, pIter);
233179234297
pIter->pSeg = pSeg;
233180234298
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 );
233182234302
}
233183234303
233184
- if( p->rc==SQLITE_OK ){
234304
+ if( p->rc==SQLITE_OK && pIter->pLeaf ){
233185234305
pIter->iLeafOffset = 4;
233186234306
assert( pIter->pLeaf!=0 );
233187234307
assert_nc( pIter->pLeaf->nn>4 );
233188234308
assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
233189234309
pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
@@ -233373,11 +234493,11 @@
233373234493
233374234494
ASSERT_SZLEAF_OK(pIter->pLeaf);
233375234495
iOff = pIter->iLeafOffset;
233376234496
233377234497
/* Next entry is on the next page */
233378
- if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
234498
+ while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
233379234499
fts5SegIterNextPage(p, pIter);
233380234500
if( p->rc || pIter->pLeaf==0 ) return;
233381234501
pIter->iRowid = 0;
233382234502
iOff = 4;
233383234503
}
@@ -233566,11 +234686,11 @@
233566234686
static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
233567234687
Fts5DlidxIter *pDlidx = pIter->pDlidx;
233568234688
Fts5Data *pLast = 0;
233569234689
int pgnoLast = 0;
233570234690
233571
- if( pDlidx ){
234691
+ if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){
233572234692
int iSegid = pIter->pSeg->iSegid;
233573234693
pgnoLast = fts5DlidxIterPgno(pDlidx);
233574234694
pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
233575234695
}else{
233576234696
Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
@@ -234127,11 +235247,12 @@
234127235247
return 0;
234128235248
}
234129235249
234130235250
/*
234131235251
** 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.
234133235254
*/
234134235255
static void fts5SegIterGotoPage(
234135235256
Fts5Index *p, /* FTS5 backend object */
234136235257
Fts5SegIter *pIter, /* Iterator to advance */
234137235258
int iLeafPgno
@@ -234142,25 +235263,27 @@
234142235263
p->rc = FTS5_CORRUPT;
234143235264
}else{
234144235265
fts5DataRelease(pIter->pNextLeaf);
234145235266
pIter->pNextLeaf = 0;
234146235267
pIter->iLeafPgno = iLeafPgno-1;
234147
- fts5SegIterNextPage(p, pIter);
234148
- assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
234149235268
234150
- if( p->rc==SQLITE_OK && ALWAYS(pIter->pLeaf!=0) ){
235269
+ while( p->rc==SQLITE_OK ){
234151235270
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;
234155235273
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;
234162235285
}
234163235286
}
234164235287
}
234165235288
}
234166235289
@@ -234871,11 +235994,11 @@
234871235994
/* Allocate space for the new multi-seg-iterator. */
234872235995
if( p->rc==SQLITE_OK ){
234873235996
if( iLevel<0 ){
234874235997
assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
234875235998
nSeg = pStruct->nSegment;
234876
- nSeg += (p->pHash ? 1 : 0);
235999
+ nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH));
234877236000
}else{
234878236001
nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
234879236002
}
234880236003
}
234881236004
*ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
@@ -234892,11 +236015,11 @@
234892236015
234893236016
/* Initialize each of the component segment iterators. */
234894236017
if( p->rc==SQLITE_OK ){
234895236018
if( iLevel<0 ){
234896236019
Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
234897
- if( p->pHash ){
236020
+ if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){
234898236021
/* Add a segment iterator for the current contents of the hash table. */
234899236022
Fts5SegIter *pIter = &pNew->aSeg[iIter++];
234900236023
fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
234901236024
}
234902236025
for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){
@@ -235647,11 +236770,11 @@
235647236770
fts5BufferZero(&buf);
235648236771
fts5BufferGrow(&p->rc, &buf, pData->nn);
235649236772
fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
235650236773
fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
235651236774
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]);
235653236776
if( p->rc==SQLITE_OK ){
235654236777
/* Set the szLeaf field */
235655236778
fts5PutU16(&buf.p[2], (u16)buf.n);
235656236779
}
235657236780
@@ -235925,20 +237048,20 @@
235925237048
Fts5Index *p, /* FTS5 backend object */
235926237049
Fts5Structure **ppStruct /* IN/OUT: Current structure of index */
235927237050
){
235928237051
const int nCrisis = p->pConfig->nCrisisMerge;
235929237052
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
+ }
235940237063
}
235941237064
235942237065
static int fts5IndexReturn(Fts5Index *p){
235943237066
int rc = p->rc;
235944237067
p->rc = SQLITE_OK;
@@ -235967,10 +237090,417 @@
235967237090
ret += i;
235968237091
}
235969237092
}
235970237093
return ret;
235971237094
}
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
+
235972237502
235973237503
/*
235974237504
** Flush the contents of in-memory hash table iHash to a new level-0
235975237505
** segment on disk. Also update the corresponding structure record.
235976237506
**
@@ -235990,10 +237520,11 @@
235990237520
fts5StructureInvalidate(p);
235991237521
235992237522
if( iSegid ){
235993237523
const int pgsz = p->pConfig->pgsz;
235994237524
int eDetail = p->pConfig->eDetail;
237525
+ int bSecureDelete = p->pConfig->bSecureDelete;
235995237526
Fts5StructureSegment *pSeg; /* New segment within pStruct */
235996237527
Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
235997237528
Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
235998237529
235999237530
Fts5SegWriter writer;
@@ -236012,44 +237543,81 @@
236012237543
if( p->rc==SQLITE_OK ){
236013237544
p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
236014237545
}
236015237546
while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
236016237547
const char *zTerm; /* Buffer containing term */
237548
+ int nTerm; /* Size of zTerm in bytes */
236017237549
const u8 *pDoclist; /* Pointer to doclist for this term */
236018237550
int nDoclist; /* Size of doclist in bytes */
236019237551
236020
- /* Write the term for this entry to disk. */
237552
+ /* Get the term and doclist for this entry. */
236021237553
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
+ }
236024237560
236025
- assert( writer.bFirstRowidInPage==0 );
236026
- if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
237561
+ if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
236027237562
/* The entire doclist will fit on the current leaf. */
236028237563
fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
236029237564
}else{
237565
+ int bTermWritten = !bSecureDelete;
236030237566
i64 iRowid = 0;
236031
- u64 iDelta = 0;
237567
+ i64 iPrev = 0;
236032237568
int iOff = 0;
236033237569
236034237570
/* The entire doclist will not fit on this leaf. The following
236035237571
** loop iterates through the poslists that make up the current
236036237572
** doclist. */
236037237573
while( p->rc==SQLITE_OK && iOff<nDoclist ){
237574
+ u64 iDelta = 0;
236038237575
iOff += fts5GetVarint(&pDoclist[iOff], &iDelta);
236039237576
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
+ }
236040237607
236041237608
if( writer.bFirstRowidInPage ){
236042237609
fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
236043237610
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
236044237611
writer.bFirstRowidInPage = 0;
236045237612
fts5WriteDlidxAppend(p, &writer, iRowid);
236046
- if( p->rc!=SQLITE_OK ) break;
236047237613
}else{
236048
- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta);
237614
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev);
236049237615
}
237616
+ if( p->rc!=SQLITE_OK ) break;
236050237617
assert( pBuf->n<=pBuf->nSpace );
237618
+ iPrev = iRowid;
236051237619
236052237620
if( eDetail==FTS5_DETAIL_NONE ){
236053237621
if( iOff<nDoclist && pDoclist[iOff]==0 ){
236054237622
pBuf->p[pBuf->n++] = 0;
236055237623
iOff++;
@@ -236104,24 +237672,27 @@
236104237672
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
236105237673
}
236106237674
sqlite3Fts5HashClear(pHash);
236107237675
fts5WriteFinish(p, &writer, &pgnoLast);
236108237676
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
+ }
236123237694
}
236124237695
236125237696
fts5IndexAutomerge(p, &pStruct, pgnoLast);
236126237697
fts5IndexCrisismerge(p, &pStruct);
236127237698
fts5StructureWrite(p, pStruct);
@@ -236858,10 +238429,11 @@
236858238429
sqlite3_finalize(p->pDeleter);
236859238430
sqlite3_finalize(p->pIdxWriter);
236860238431
sqlite3_finalize(p->pIdxDeleter);
236861238432
sqlite3_finalize(p->pIdxSelect);
236862238433
sqlite3_finalize(p->pDataVersion);
238434
+ sqlite3_finalize(p->pDeleteFromIdx);
236863238435
sqlite3Fts5HashFree(p->pHash);
236864238436
sqlite3_free(p->zDataTbl);
236865238437
sqlite3_free(p);
236866238438
}
236867238439
return rc;
@@ -237488,10 +239060,11 @@
237488239060
static void fts5IndexIntegrityCheckSegment(
237489239061
Fts5Index *p, /* FTS5 backend object */
237490239062
Fts5StructureSegment *pSeg /* Segment to check internal consistency */
237491239063
){
237492239064
Fts5Config *pConfig = p->pConfig;
239065
+ int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE);
237493239066
sqlite3_stmt *pStmt = 0;
237494239067
int rc2;
237495239068
int iIdxPrevLeaf = pSeg->pgnoFirst-1;
237496239069
int iDlidxPrevLeaf = pSeg->pgnoLast;
237497239070
@@ -237523,11 +239096,23 @@
237523239096
/* Check that the leaf contains at least one term, and that it is equal
237524239097
** to or larger than the split-key in zIdxTerm. Also check that if there
237525239098
** is also a rowid pointer within the leaf page header, it points to a
237526239099
** location before the term. */
237527239100
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
+
237529239114
}else{
237530239115
int iOff; /* Offset of first term on leaf */
237531239116
int iRowidOff; /* Offset of first rowid on leaf */
237532239117
int nTerm; /* Size of term on leaf in bytes */
237533239118
int res; /* Comparison of term and split-key */
@@ -237587,13 +239172,16 @@
237587239172
i64 iRowid;
237588239173
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
237589239174
ASSERT_SZLEAF_OK(pLeaf);
237590239175
if( iRowidOff>=pLeaf->szLeaf ){
237591239176
p->rc = FTS5_CORRUPT;
237592
- }else{
239177
+ }else if( bSecureDelete==0 || iRowidOff>0 ){
239178
+ i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
237593239179
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
+ }
237595239183
}
237596239184
fts5DataRelease(pLeaf);
237597239185
}
237598239186
}
237599239187
@@ -239851,10 +241439,12 @@
239851241439
){
239852241440
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
239853241441
Fts5Config *pConfig = pTab->p.pConfig;
239854241442
int eType0; /* value_type() of apVal[0] */
239855241443
int rc = SQLITE_OK; /* Return code */
241444
+ int bUpdateOrDelete = 0;
241445
+
239856241446
239857241447
/* A transaction must be open when this is called. */
239858241448
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
239859241449
239860241450
assert( pVtab->zErrMsg==0 );
@@ -239861,10 +241451,15 @@
239861241451
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
239862241452
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER
239863241453
|| sqlite3_value_type(apVal[0])==SQLITE_NULL
239864241454
);
239865241455
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
+
239866241461
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
239867241462
239868241463
/* Put any active cursors into REQUIRE_SEEK state. */
239869241464
fts5TripCursors(pTab);
239870241465
@@ -239913,10 +241508,11 @@
239913241508
239914241509
/* DELETE */
239915241510
else if( nArg==1 ){
239916241511
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
239917241512
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
241513
+ bUpdateOrDelete = 1;
239918241514
}
239919241515
239920241516
/* INSERT or UPDATE */
239921241517
else{
239922241518
int eType1 = sqlite3_value_numeric_type(apVal[1]);
@@ -239928,10 +241524,11 @@
239928241524
else if( eType0!=SQLITE_INTEGER ){
239929241525
/* If this is a REPLACE, first remove the current entry (if any) */
239930241526
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
239931241527
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
239932241528
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
241529
+ bUpdateOrDelete = 1;
239933241530
}
239934241531
fts5StorageInsert(&rc, pTab, apVal, pRowid);
239935241532
}
239936241533
239937241534
/* UPDATE */
@@ -239956,13 +241553,27 @@
239956241553
}
239957241554
}else{
239958241555
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
239959241556
fts5StorageInsert(&rc, pTab, apVal, pRowid);
239960241557
}
241558
+ bUpdateOrDelete = 1;
239961241559
}
239962241560
}
239963241561
}
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
+ }
239964241575
239965241576
pTab->p.pConfig->pzErrmsg = 0;
239966241577
return rc;
239967241578
}
239968241579
@@ -240819,10 +242430,11 @@
240819242430
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
240820242431
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
240821242432
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
240822242433
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
240823242434
fts5TripCursors(pTab);
242435
+ pTab->p.pConfig->pgsz = 0;
240824242436
return sqlite3Fts5StorageRollback(pTab->pStorage);
240825242437
}
240826242438
240827242439
/*
240828242440
** Register a new auxiliary function with global context pGlobal.
@@ -241021,11 +242633,11 @@
241021242633
int nArg, /* Number of args */
241022242634
sqlite3_value **apUnused /* Function arguments */
241023242635
){
241024242636
assert( nArg==0 );
241025242637
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);
241027242639
}
241028242640
241029242641
/*
241030242642
** Return true if zName is the extension on one of the shadow tables used
241031242643
** by this module.
241032242644
--- 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 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.42.0"
150150
#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"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -2140,32 +2140,32 @@
21402140
** configuration setting is never used, then the default maximum is determined
21412141
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
21422142
** compile-time option is not set, then the default maximum is 1073741824.
21432143
** </dl>
21442144
*/
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* */
21642164
#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 */
21672167
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
21682168
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
21692169
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
21702170
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
21712171
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
@@ -2396,29 +2396,29 @@
23962396
** additional information. This feature can also be turned on and off
23972397
** using the [PRAGMA legacy_alter_table] statement.
23982398
** </dd>
23992399
**
24002400
** [[SQLITE_DBCONFIG_DQS_DML]]
2401
-** <dt>SQLITE_DBCONFIG_DQS_DML</td>
2401
+** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
24022402
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
24032403
** the legacy [double-quoted string literal] misfeature for DML statements
24042404
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
24052405
** default value of this setting is determined by the [-DSQLITE_DQS]
24062406
** compile-time option.
24072407
** </dd>
24082408
**
24092409
** [[SQLITE_DBCONFIG_DQS_DDL]]
2410
-** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
2410
+** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
24112411
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
24122412
** the legacy [double-quoted string literal] misfeature for DDL statements,
24132413
** such as CREATE TABLE and CREATE INDEX. The
24142414
** default value of this setting is determined by the [-DSQLITE_DQS]
24152415
** compile-time option.
24162416
** </dd>
24172417
**
24182418
** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
2419
-** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td>
2419
+** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
24202420
** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
24212421
** assume that database schemas are untainted by malicious content.
24222422
** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
24232423
** takes additional defensive steps to protect the application from harm
24242424
** including:
@@ -2434,20 +2434,20 @@
24342434
** all applications are advised to turn it off if possible. This setting
24352435
** can also be controlled using the [PRAGMA trusted_schema] statement.
24362436
** </dd>
24372437
**
24382438
** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
2439
-** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td>
2439
+** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
24402440
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
24412441
** the legacy file format flag. When activated, this flag causes all newly
24422442
** created database file to have a schema format version number (the 4-byte
24432443
** integer found at offset 44 into the database header) of 1. This in turn
24442444
** means that the resulting database file will be readable and writable by
24452445
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
24462446
** newly created databases are generally not understandable by SQLite versions
24472447
** 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
24492449
** all the way back to version 3.0.0, and so this setting is of little
24502450
** practical use, but is provided so that SQLite can continue to claim the
24512451
** ability to generate new database files that are compatible with version
24522452
** 3.0.0.
24532453
** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on,
@@ -2456,27 +2456,39 @@
24562456
** not considered a bug since SQLite versions 3.3.0 and earlier do not support
24572457
** either generated columns or decending indexes.
24582458
** </dd>
24592459
**
24602460
** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
2461
-** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</td>
2461
+** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
24622462
** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
24632463
** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
24642464
** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
24652465
** statistics. For statistics to be collected, the flag must be set on
24662466
** the database handle both when the SQL statement is prepared and when it
24672467
** 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>
24692475
**
24702476
** [[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
24732479
** in which tables and indexes are scanned so that the scans start at the end
24742480
** 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>
24782490
**
24792491
** </dl>
24802492
*/
24812493
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
24822494
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@@ -2494,11 +2506,11 @@
24942506
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
24952507
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
24962508
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
24972509
#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
24982510
#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* */
25002512
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
25012513
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
25022514
25032515
/*
25042516
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -6241,10 +6253,17 @@
62416253
** ^SQLite implements this interface by calling the xSleep()
62426254
** method of the default [sqlite3_vfs] object. If the xSleep() method
62436255
** of the default VFS is not implemented correctly, or not implemented at
62446256
** all, then the behavior of sqlite3_sleep() may deviate from the description
62456257
** 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.
62466265
*/
62476266
SQLITE_API int sqlite3_sleep(int);
62486267
62496268
/*
62506269
** CAPI3REF: Name Of The Folder Holding Temporary Files
@@ -7868,13 +7887,13 @@
78687887
** ^The sqlite3_mutex_leave() routine exits a mutex that was
78697888
** previously entered by the same thread. The behavior
78707889
** is undefined if the mutex is not currently entered by the
78717890
** calling thread or is not currently allocated.
78727891
**
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.
78767895
**
78777896
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
78787897
*/
78797898
SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int);
78807899
SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*);
@@ -10800,20 +10819,24 @@
1080010819
** [sqlite3session_create()] for details.
1080110820
*/
1080210821
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
1080310822
1080410823
/*
10805
-** CAPIREF: Conigure a Session Object
10824
+** CAPI3REF: Configure a Session Object
1080610825
** METHOD: sqlite3_session
1080710826
**
1080810827
** 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].
1081110830
**
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
1081310836
**
10814
-** The following values may passed as the the 4th parameter to
10837
+** The following values may passed as the the 2nd parameter to
1081510838
** sqlite3session_object_config().
1081610839
**
1081710840
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
1081810841
** This option is used to set, clear or query the flag that enables
1081910842
** the [sqlite3session_changeset_size()] API. Because it imposes some
@@ -10825,16 +10848,25 @@
1082510848
** variable is set to 1 if the sqlite3session_changeset_size() API is
1082610849
** enabled following the current call, or 0 otherwise.
1082710850
**
1082810851
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
1082910852
** 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.
1083410865
*/
10835
-#define SQLITE_SESSION_OBJCONFIG_SIZE 1
10866
+#define SQLITE_SESSION_OBJCONFIG_SIZE 1
10867
+#define SQLITE_SESSION_OBJCONFIG_ROWID 2
1083610868
1083710869
/*
1083810870
** CAPI3REF: Enable Or Disable A Session Object
1083910871
** METHOD: sqlite3_session
1084010872
**
1084110873
--- 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
--- skins/ardoise/css.txt
+++ skins/ardoise/css.txt
@@ -1387,11 +1387,16 @@
13871387
}
13881388
.intLink[title=Hyperlink]:hover {
13891389
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=)
13901390
}
13911391
.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;
13931398
}
13941399
mark,
13951400
p.noMoreShun,
13961401
p.shunned,
13971402
span.modpending {
13981403
--- 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
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -416,11 +416,16 @@
416416
table.numbered-lines td.line-numbers span.selected-line {
417417
background-color: #7EA2D9;
418418
}
419419
420420
.statistics-report-graph-line {
421
+ border: 2px solid #7EA2D9;
421422
background-color: #7EA2D9;
423
+}
424
+.statistics-report-graph-extra {
425
+ border: 2px solid #7EA2D9;
426
+ border-left-style: none;
422427
}
423428
424429
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
425430
background-color: #455978;
426431
}
427432
--- 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
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -513,11 +513,16 @@
513513
/**************************************
514514
* Statistics Reports
515515
*/
516516
517517
.statistics-report-graph-line {
518
+ border: 2px solid #22e;
518519
background-color: #22e;
520
+}
521
+.statistics-report-graph-extra {
522
+ border: 2px dashed #22e;
523
+ border-left-style: none;
519524
}
520525
521526
.statistics-report-table-events th {
522527
padding: 0 1rem;
523528
}
524529
--- 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 @@
186186
" ON CONFLICT(eventId) DO NOTHING;\n"
187187
"END;"
188188
);
189189
}
190190
if( db_table_exists("repository","chat")
191
- && db_get("chat-timeline-user", "")[0]!=0
191
+ && db_get("chat-timeline-user", "")[0]!=0
192192
){
193193
/* Record events that will be relayed to chat, but do not relay
194194
** them immediately, as the chat_msg_from_event() function requires
195195
** that TAGXREF be up-to-date, and that has not happened yet when
196196
** the insert into the EVENT table occurs. Make arrangements to
@@ -233,10 +233,26 @@
233233
int alert_enabled(void){
234234
if( !alert_tables_exist() ) return 0;
235235
if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0;
236236
return 1;
237237
}
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
+}
238254
239255
/*
240256
** If the subscriber table does not exist, then paint an error message
241257
** web page and return true.
242258
**
@@ -301,11 +317,11 @@
301317
}
302318
@ </table>
303319
@ <hr>
304320
@ <h1> Configuration </h1>
305321
@ <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>
307323
login_insert_csrf_secret();
308324
309325
entry_attribute("Canonical Server URL", 40, "email-url",
310326
"eurl", "", 0);
311327
@ <p><b>Required.</b>
@@ -400,11 +416,11 @@
400416
@ append a colon and TCP port number (ex: smtp.example.com:587).
401417
@ The default TCP port number is 25.
402418
@ (Property: "email-send-relayhost")</p>
403419
@ <hr>
404420
405
- @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
421
+ @ <p><input type="submit" name="submit" value="Apply Changes"></p>
406422
@ </div></form>
407423
db_end_transaction(0);
408424
style_finish_page();
409425
}
410426
411427
--- 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 @@
111111
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
112112
}
113113
@ <li><p>
114114
@ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
115115
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>
117117
@ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
118118
if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
119119
if( zComment && zComment[0] ){
120
- @ %!W(zComment)<br />
120
+ @ %!W(zComment)<br>
121121
}
122122
if( zPage==0 && zTkt==0 && zTechNote==0 ){
123123
if( zSrc==0 || zSrc[0]==0 ){
124124
zSrc = "Deleted from";
125125
}else {
@@ -397,23 +397,23 @@
397397
}
398398
@ <h2>Add Attachment To %s(zTargetType)</h2>
399399
form_begin("enctype='multipart/form-data'", "%R/attachadd");
400400
@ <div>
401401
@ 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>
405405
if( zTkt ){
406
- @ <input type="hidden" name="tkt" value="%h(zTkt)" />
406
+ @ <input type="hidden" name="tkt" value="%h(zTkt)">
407407
}else if( zTechNote ){
408
- @ <input type="hidden" name="technote" value="%h(zTechNote)" />
408
+ @ <input type="hidden" name="technote" value="%h(zTechNote)">
409409
}else{
410
- @ <input type="hidden" name="page" value="%h(zPage)" />
410
+ @ <input type="hidden" name="page" value="%h(zPage)">
411411
}
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">
415415
@ </div>
416416
captcha_generate(0);
417417
@ </form>
418418
style_finish_page();
419419
fossil_free(zTargetType);
@@ -591,13 +591,13 @@
591591
if( isModerator && modPending ){
592592
@ <div class="section">Moderation</div>
593593
@ <blockquote>
594594
form_begin(0, "%R/ainfo/%s", zUuid);
595595
@ <label><input type="radio" name="modaction" value="delete">
596
- @ Delete this change</label><br />
596
+ @ Delete this change</label><br>
597597
@ <label><input type="radio" name="modaction" value="approve">
598
- @ Approve this change</label><br />
598
+ @ Approve this change</label><br>
599599
@ <input type="submit" value="Submit">
600600
@ </form>
601601
@ </blockquote>
602602
}
603603
@@ -616,11 +616,11 @@
616616
@ %h(z)
617617
@ </pre>
618618
}
619619
}else if( strncmp(zMime, "image/", 6)==0 ){
620620
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>
622622
@ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img>
623623
style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime);
624624
}else{
625625
int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
626626
@ <i>(file is %d(sz) bytes of binary data)</i>
627627
--- 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 @@
111111
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
112112
}
113113
@ <li><p>
114114
@ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
115115
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>
117117
@ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
118118
if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
119119
if( zComment && zComment[0] ){
120
- @ %!W(zComment)<br />
120
+ @ %!W(zComment)<br>
121121
}
122122
if( zPage==0 && zTkt==0 && zTechNote==0 ){
123123
if( zSrc==0 || zSrc[0]==0 ){
124124
zSrc = "Deleted from";
125125
}else {
@@ -397,23 +397,23 @@
397397
}
398398
@ <h2>Add Attachment To %s(zTargetType)</h2>
399399
form_begin("enctype='multipart/form-data'", "%R/attachadd");
400400
@ <div>
401401
@ 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>
405405
if( zTkt ){
406
- @ <input type="hidden" name="tkt" value="%h(zTkt)" />
406
+ @ <input type="hidden" name="tkt" value="%h(zTkt)">
407407
}else if( zTechNote ){
408
- @ <input type="hidden" name="technote" value="%h(zTechNote)" />
408
+ @ <input type="hidden" name="technote" value="%h(zTechNote)">
409409
}else{
410
- @ <input type="hidden" name="page" value="%h(zPage)" />
410
+ @ <input type="hidden" name="page" value="%h(zPage)">
411411
}
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">
415415
@ </div>
416416
captcha_generate(0);
417417
@ </form>
418418
style_finish_page();
419419
fossil_free(zTargetType);
@@ -591,13 +591,13 @@
591591
if( isModerator && modPending ){
592592
@ <div class="section">Moderation</div>
593593
@ <blockquote>
594594
form_begin(0, "%R/ainfo/%s", zUuid);
595595
@ <label><input type="radio" name="modaction" value="delete">
596
- @ Delete this change</label><br />
596
+ @ Delete this change</label><br>
597597
@ <label><input type="radio" name="modaction" value="approve">
598
- @ Approve this change</label><br />
598
+ @ Approve this change</label><br>
599599
@ <input type="submit" value="Submit">
600600
@ </form>
601601
@ </blockquote>
602602
}
603603
@@ -616,11 +616,11 @@
616616
@ %h(z)
617617
@ </pre>
618618
}
619619
}else if( strncmp(zMime, "image/", 6)==0 ){
620620
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>
622622
@ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img>
623623
style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime);
624624
}else{
625625
int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
626626
@ <i>(file is %d(sz) bytes of binary data)</i>
627627
--- 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 @@
851851
if( pTo ){
852852
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
853853
}
854854
pFrom->iCursor = i;
855855
}
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
+}
856943
857944
/*
858945
** Ensure that the text in pBlob ends with '\n'
859946
*/
860947
void blob_add_final_newline(Blob *pBlob){
861948
--- 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 @@
11641164
while( db_step(&q2)==SQLITE_ROW ){
11651165
const char *zFile = db_column_text(&q2,0);
11661166
@ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
11671167
if( showId ){
11681168
int fid = db_column_int(&q2,1);
1169
- @ (%d(fid))<br />
1169
+ @ (%d(fid))<br>
11701170
}else{
1171
- @ </a><br />
1171
+ @ </a><br>
11721172
}
11731173
}
11741174
db_reset(&q2);
11751175
@ </td>
11761176
@ <td>
11771177
--- 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 @@
423423
if( pStmt ){
424424
@ <ol>
425425
while( sqlite3_step(pStmt)==SQLITE_ROW ){
426426
const unsigned char *zName = sqlite3_column_text(pStmt,0);
427427
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>
429429
@ size: %,lld(sqlite3_column_int64(pStmt,1))
430430
@ hit-count: %d(sqlite3_column_int(pStmt,2))
431431
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
432432
if( zHash ){
433433
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
434434
--- 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 @@
544544
zCaptcha = captcha_render(zDecoded);
545545
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
546546
@ %h(zCaptcha)
547547
@ </pre>
548548
@ 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>
551551
if( showButton ){
552552
@ <input type="submit" value="Submit">
553553
}
554554
@ <br/>\
555555
captcha_speakit_button(uSeed, 0);
@@ -685,11 +685,11 @@
685685
**
686686
** Return a WAV file that pronounces the digits of the captcha that
687687
** is determined by the seed given in the name= query parameter.
688688
*/
689689
void captcha_wav_page(void){
690
- const char *zSeed = P("name");
690
+ const char *zSeed = PD("name","0");
691691
const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
692692
Blob audio;
693693
captcha_wav(zDecode, &audio);
694694
cgi_set_content_type("audio/wav");
695695
cgi_set_content(&audio);
696696
--- 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 @@
17471747
if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
17481748
if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
17491749
}
17501750
switch( eDest ){
17511751
case 0: {
1752
- cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1752
+ cgi_printf("%h = %h <br>\n", zName, aParamQP[i].zValue);
17531753
break;
17541754
}
17551755
case 1: {
17561756
fossil_trace("%s = %s\n", zName, aParamQP[i].zValue);
17571757
break;
@@ -2052,10 +2052,12 @@
20522052
cgi_setenv("HTTP_REFERER", zVal);
20532053
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
20542054
cgi_setenv("HTTP_USER_AGENT", zVal);
20552055
}else if( fossil_strcmp(zFieldName,"authorization:")==0 ){
20562056
cgi_setenv("HTTP_AUTHORIZATION", zVal);
2057
+ }else if( fossil_strcmp(zFieldName,"accept-language:")==0 ){
2058
+ cgi_setenv("HTTP_ACCEPT_LANGUAGE", zVal);
20572059
}else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){
20582060
const char *zIpAddr = cgi_accept_forwarded_for(zVal);
20592061
if( zIpAddr!=0 ){
20602062
g.zIpAddr = fossil_strdup(zIpAddr);
20612063
cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr);
20622064
--- 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 @@
18691869
int lookFlags; /* output flags from looks_like_utf8/utf16() */
18701870
int fHasAnyCr; /* the blob contains one or more CR chars */
18711871
int fHasLoneCrOnly; /* all detected line endings are CR only */
18721872
int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */
18731873
int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */
1874
+ int fHasNul; /* contains NUL chars? */
1875
+ int fHasLong; /* overly long line? */
18741876
char *zMsg; /* Warning message */
18751877
Blob fname; /* Relative pathname of the file */
18761878
static int allOk = 0; /* Set to true to disable this routine */
18771879
18781880
if( allOk ) return 0;
@@ -1888,13 +1890,15 @@
18881890
}
18891891
fHasAnyCr = (lookFlags & LOOK_CR);
18901892
fBinary = (lookFlags & LOOK_BINARY);
18911893
fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR);
18921894
fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF);
1895
+ fHasNul = (lookFlags & LOOK_NUL);
1896
+ fHasLong = (lookFlags & LOOK_LONG);
18931897
}else{
18941898
fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0;
1895
- fHasLoneCrOnly = fHasCrLfOnly = 0;
1899
+ fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0;
18961900
}
18971901
if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){
18981902
const char *zWarning = 0;
18991903
const char *zDisable = 0;
19001904
const char *zConvert = "c=convert/";
@@ -1901,12 +1905,10 @@
19011905
const char *zIn = "in";
19021906
Blob ans;
19031907
char cReply;
19041908
19051909
if( fBinary ){
1906
- int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */
1907
- int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */
19081910
if( binOk ){
19091911
return 0; /* We don't want binary warnings for this file. */
19101912
}
19111913
if( !fHasNul && fHasLong ){
19121914
zWarning = "long lines";
19131915
--- 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 @@
18691869
int lookFlags; /* output flags from looks_like_utf8/utf16() */
18701870
int fHasAnyCr; /* the blob contains one or more CR chars */
18711871
int fHasLoneCrOnly; /* all detected line endings are CR only */
18721872
int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */
18731873
int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */
1874
+ int fHasNul; /* contains NUL chars? */
1875
+ int fHasLong; /* overly long line? */
18741876
char *zMsg; /* Warning message */
18751877
Blob fname; /* Relative pathname of the file */
18761878
static int allOk = 0; /* Set to true to disable this routine */
18771879
18781880
if( allOk ) return 0;
@@ -1888,13 +1890,15 @@
18881890
}
18891891
fHasAnyCr = (lookFlags & LOOK_CR);
18901892
fBinary = (lookFlags & LOOK_BINARY);
18911893
fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR);
18921894
fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF);
1895
+ fHasNul = (lookFlags & LOOK_NUL);
1896
+ fHasLong = (lookFlags & LOOK_LONG);
18931897
}else{
18941898
fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0;
1895
- fHasLoneCrOnly = fHasCrLfOnly = 0;
1899
+ fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0;
18961900
}
18971901
if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){
18981902
const char *zWarning = 0;
18991903
const char *zDisable = 0;
19001904
const char *zConvert = "c=convert/";
@@ -1901,12 +1905,10 @@
19011905
const char *zIn = "in";
19021906
Blob ans;
19031907
char cReply;
19041908
19051909
if( fBinary ){
1906
- int fHasNul = (lookFlags & LOOK_NUL); /* contains NUL chars? */
1907
- int fHasLong = (lookFlags & LOOK_LONG); /* overly long line? */
19081910
if( binOk ){
19091911
return 0; /* We don't want binary warnings for this file. */
19101912
}
19111913
if( !fHasNul && fHasLong ){
19121914
zWarning = "long lines";
19131915
--- 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 @@
161161
@ suam declinavit.</p>
162162
cnt++;
163163
}
164164
}
165165
if( cnt ){
166
- @ <hr />
166
+ @ <hr>
167167
}
168168
@ <form method="POST">
169169
@ <p>Enter candidate branch names below and see them displayed in their
170170
@ default background colors above.</p>
171171
for(i=0; i<10; i++){
172172
sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
173173
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>
175175
}
176176
@ <input type="submit" value="Submit">
177177
@ <input type="submit" name="rand" value="Random">
178178
@ </form>
179179
style_finish_page();
180180
--- 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
+405 -65
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
3131
*/
3232
#include "config.h"
3333
#if defined(_WIN32)
3434
# if USE_SEE
3535
# include <windows.h>
36
+# define GETPID (int)GetCurrentProcessId
3637
# endif
3738
#else
3839
# include <pwd.h>
40
+# if USE_SEE
41
+# define GETPID getpid
42
+# endif
3943
#endif
4044
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
4145
# define SQLITE_HAS_CODEC
46
+#endif
47
+#if USE_SEE && defined(__linux__)
48
+# include <sys/uio.h>
4249
#endif
4350
#include <sqlite3.h>
4451
#include <sys/types.h>
4552
#include <sys/stat.h>
4653
#include <unistd.h>
4754
#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
+
4865
#include "db.h"
4966
5067
#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
+
5183
/*
5284
** An single SQL statement is represented as an instance of the following
5385
** structure.
5486
*/
5587
struct Stmt {
@@ -1539,11 +1571,30 @@
15391571
static char *zSavedKey = 0;
15401572
15411573
/*
15421574
** This is the size of the saved database encryption key, in bytes.
15431575
*/
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
+}
15451596
15461597
/*
15471598
** This function returns the saved database encryption key -OR- zero if
15481599
** no database encryption key is saved.
15491600
*/
@@ -1556,10 +1607,36 @@
15561607
** -OR- zero if no database encryption key is saved.
15571608
*/
15581609
size_t db_get_saved_encryption_key_size(){
15591610
return savedKeySize;
15601611
}
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
+}
15611638
15621639
/*
15631640
** This function arranges for the database encryption key to be securely
15641641
** saved in non-pagable memory (on platforms where this is possible).
15651642
*/
@@ -1569,10 +1646,11 @@
15691646
void *p = NULL;
15701647
size_t n = 0;
15711648
size_t pageSize = 0;
15721649
size_t blobSize = 0;
15731650
1651
+ assert( !db_have_saved_encryption_key() );
15741652
blobSize = blob_size(pKey);
15751653
if( blobSize==0 ) return;
15761654
fossil_get_page_size(&pageSize);
15771655
assert( pageSize>0 );
15781656
if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
15991677
16001678
/*
16011679
** This function sets the saved database encryption key to the specified
16021680
** string value, allocating or freeing the underlying memory if needed.
16031681
*/
1604
-void db_set_saved_encryption_key(
1682
+static void db_set_saved_encryption_key(
16051683
Blob *pKey
16061684
){
16071685
if( zSavedKey!=NULL ){
16081686
size_t blobSize = blob_size(pKey);
16091687
if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
16191697
}else{
16201698
db_save_encryption_key(pKey);
16211699
}
16221700
}
16231701
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
+ {
16241855
#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
+
16251893
/*
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.
16291897
*/
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. */
16321900
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
16331901
SIZE_T nSize /* Size of saved key buffer in the parent process. */
16341902
){
16351903
void *p = NULL;
16361904
size_t n = 0;
16371905
size_t pageSize = 0;
1638
- HANDLE hProcess = NULL;
16391906
16401907
fossil_get_page_size(&pageSize);
16411908
assert( pageSize>0 );
16421909
if( nSize>pageSize ){
16431910
fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
16441911
}
16451912
p = fossil_secure_alloc_page(&n);
16461913
assert( p!=NULL );
16471914
assert( n==pageSize );
16481915
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
16691957
}
16701958
}
16711959
16721960
/*
16731961
** This function evaluates the specified TH1 script and attempts to parse
16741962
** its result as a colon-delimited triplet containing a process identifier,
16751963
** 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.
16771965
*/
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. */
16801970
){
1971
+ PID_T processId = 0;
16811972
int rc;
16821973
char *zResult;
16831974
char *zPwd = file_getcwd(0, 0);
16841975
Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
16851976
rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
16861977
zResult = (char*)Th_GetResult(g.interp, 0);
16871978
if( rc!=TH_OK ){
16881979
fossil_fatal("script for pid key failed: %s", zResult);
16891980
}
16901981
if( zResult ){
1691
- DWORD processId = 0;
16921982
LPVOID pAddress = NULL;
16931983
SIZE_T nSize = 0;
16941984
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
+ }
16961994
}
16971995
file_chdir(zPwd, 0);
16981996
fossil_free(zPwd);
1997
+ return processId;
16991998
}
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
+}
17012037
#endif /* USE_SEE */
17022038
17032039
/*
17042040
** If the database file zDbFile has a name that suggests that it is
17052041
** encrypted, then prompt for the database encryption key and return it
@@ -3294,10 +3630,11 @@
32943630
noWarn = 1;
32953631
}
32963632
}
32973633
blob_reset(&versionedPathname);
32983634
if( found ){
3635
+ blob_strip_comment_lines(&setting, &setting);
32993636
blob_trim(&setting); /* Avoid non-obvious problems with line endings
33003637
** on boolean properties */
33013638
zVersionedSetting = fossil_strdup(blob_str(&setting));
33023639
}
33033640
blob_reset(&setting);
@@ -4158,13 +4495,14 @@
41584495
** every time the backoffice runs. This can be used for debugging,
41594496
** to ensure that backoffice is running appropriately.
41604497
*/
41614498
/*
41624499
** 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
41664504
*/
41674505
#if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__)
41684506
/*
41694507
** SETTING: case-sensitive boolean default=off
41704508
** If TRUE, the files whose names differ only in case
@@ -4182,14 +4520,14 @@
41824520
** TRUE for unix and FALSE for Cygwin, Mac and Windows.
41834521
*/
41844522
#endif
41854523
/*
41864524
** 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
41914529
*/
41924530
/*
41934531
** SETTING: clearsign boolean default=off
41944532
** When enabled, fossil will attempt to sign all commits
41954533
** with gpg. When disabled, commits will be unsigned.
@@ -4217,13 +4555,14 @@
42174555
** Note: The options for timeline comments displayed on the web UI can be
42184556
** configured through the /setup_timeline web page.
42194557
*/
42204558
/*
42214559
** 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
42254564
** The crnl-glob setting is a compatibility alias.
42264565
*/
42274566
/*
42284567
** SETTING: crnl-glob width=40 versionable block-text
42294568
** This is an alias for the crlf-glob setting.
@@ -4265,22 +4604,22 @@
42654604
** The value is an external command that will launch the
42664605
** text editor command used for check-in comments.
42674606
*/
42684607
/*
42694608
** 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.
42744613
*/
42754614
/*
42764615
** 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
42824621
*/
42834622
#if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
42844623
/*
42854624
** SETTING: exec-rel-paths boolean default=on
42864625
** When executing certain external commands (e.g. diff and
@@ -4295,14 +4634,15 @@
42954634
*/
42964635
#endif
42974636
42984637
/*
42994638
** 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
43044644
** contain any globs for, e.g., images or PDFs.
43054645
*/
43064646
/*
43074647
** SETTING: forbid-delta-manifests boolean default=off
43084648
** If enabled on a client, new delta manifests are prohibited on
@@ -4340,21 +4680,21 @@
43404680
** If true, then the Fossil web server will redirect unencrypted
43414681
** login screen requests to HTTPS.
43424682
*/
43434683
/*
43444684
** 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
43504689
*/
43514690
/*
43524691
** 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
43564696
*/
43574697
/*
43584698
** SETTING: localauth boolean default=off
43594699
** If enabled, require that HTTP connections from the loopback
43604700
** address (127.0.0.1) be authenticated by password. If false,
43614701
--- 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
+405 -65
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
3131
*/
3232
#include "config.h"
3333
#if defined(_WIN32)
3434
# if USE_SEE
3535
# include <windows.h>
36
+# define GETPID (int)GetCurrentProcessId
3637
# endif
3738
#else
3839
# include <pwd.h>
40
+# if USE_SEE
41
+# define GETPID getpid
42
+# endif
3943
#endif
4044
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
4145
# define SQLITE_HAS_CODEC
46
+#endif
47
+#if USE_SEE && defined(__linux__)
48
+# include <sys/uio.h>
4249
#endif
4350
#include <sqlite3.h>
4451
#include <sys/types.h>
4552
#include <sys/stat.h>
4653
#include <unistd.h>
4754
#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
+
4865
#include "db.h"
4966
5067
#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
+
5183
/*
5284
** An single SQL statement is represented as an instance of the following
5385
** structure.
5486
*/
5587
struct Stmt {
@@ -1539,11 +1571,30 @@
15391571
static char *zSavedKey = 0;
15401572
15411573
/*
15421574
** This is the size of the saved database encryption key, in bytes.
15431575
*/
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
+}
15451596
15461597
/*
15471598
** This function returns the saved database encryption key -OR- zero if
15481599
** no database encryption key is saved.
15491600
*/
@@ -1556,10 +1607,36 @@
15561607
** -OR- zero if no database encryption key is saved.
15571608
*/
15581609
size_t db_get_saved_encryption_key_size(){
15591610
return savedKeySize;
15601611
}
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
+}
15611638
15621639
/*
15631640
** This function arranges for the database encryption key to be securely
15641641
** saved in non-pagable memory (on platforms where this is possible).
15651642
*/
@@ -1569,10 +1646,11 @@
15691646
void *p = NULL;
15701647
size_t n = 0;
15711648
size_t pageSize = 0;
15721649
size_t blobSize = 0;
15731650
1651
+ assert( !db_have_saved_encryption_key() );
15741652
blobSize = blob_size(pKey);
15751653
if( blobSize==0 ) return;
15761654
fossil_get_page_size(&pageSize);
15771655
assert( pageSize>0 );
15781656
if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
15991677
16001678
/*
16011679
** This function sets the saved database encryption key to the specified
16021680
** string value, allocating or freeing the underlying memory if needed.
16031681
*/
1604
-void db_set_saved_encryption_key(
1682
+static void db_set_saved_encryption_key(
16051683
Blob *pKey
16061684
){
16071685
if( zSavedKey!=NULL ){
16081686
size_t blobSize = blob_size(pKey);
16091687
if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
16191697
}else{
16201698
db_save_encryption_key(pKey);
16211699
}
16221700
}
16231701
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
+ {
16241855
#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
+
16251893
/*
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.
16291897
*/
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. */
16321900
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
16331901
SIZE_T nSize /* Size of saved key buffer in the parent process. */
16341902
){
16351903
void *p = NULL;
16361904
size_t n = 0;
16371905
size_t pageSize = 0;
1638
- HANDLE hProcess = NULL;
16391906
16401907
fossil_get_page_size(&pageSize);
16411908
assert( pageSize>0 );
16421909
if( nSize>pageSize ){
16431910
fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
16441911
}
16451912
p = fossil_secure_alloc_page(&n);
16461913
assert( p!=NULL );
16471914
assert( n==pageSize );
16481915
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
16691957
}
16701958
}
16711959
16721960
/*
16731961
** This function evaluates the specified TH1 script and attempts to parse
16741962
** its result as a colon-delimited triplet containing a process identifier,
16751963
** 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.
16771965
*/
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. */
16801970
){
1971
+ PID_T processId = 0;
16811972
int rc;
16821973
char *zResult;
16831974
char *zPwd = file_getcwd(0, 0);
16841975
Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
16851976
rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
16861977
zResult = (char*)Th_GetResult(g.interp, 0);
16871978
if( rc!=TH_OK ){
16881979
fossil_fatal("script for pid key failed: %s", zResult);
16891980
}
16901981
if( zResult ){
1691
- DWORD processId = 0;
16921982
LPVOID pAddress = NULL;
16931983
SIZE_T nSize = 0;
16941984
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
+ }
16961994
}
16971995
file_chdir(zPwd, 0);
16981996
fossil_free(zPwd);
1997
+ return processId;
16991998
}
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
+}
17012037
#endif /* USE_SEE */
17022038
17032039
/*
17042040
** If the database file zDbFile has a name that suggests that it is
17052041
** encrypted, then prompt for the database encryption key and return it
@@ -3294,10 +3630,11 @@
32943630
noWarn = 1;
32953631
}
32963632
}
32973633
blob_reset(&versionedPathname);
32983634
if( found ){
3635
+ blob_strip_comment_lines(&setting, &setting);
32993636
blob_trim(&setting); /* Avoid non-obvious problems with line endings
33003637
** on boolean properties */
33013638
zVersionedSetting = fossil_strdup(blob_str(&setting));
33023639
}
33033640
blob_reset(&setting);
@@ -4158,13 +4495,14 @@
41584495
** every time the backoffice runs. This can be used for debugging,
41594496
** to ensure that backoffice is running appropriately.
41604497
*/
41614498
/*
41624499
** 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
41664504
*/
41674505
#if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__)
41684506
/*
41694507
** SETTING: case-sensitive boolean default=off
41704508
** If TRUE, the files whose names differ only in case
@@ -4182,14 +4520,14 @@
41824520
** TRUE for unix and FALSE for Cygwin, Mac and Windows.
41834521
*/
41844522
#endif
41854523
/*
41864524
** 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
41914529
*/
41924530
/*
41934531
** SETTING: clearsign boolean default=off
41944532
** When enabled, fossil will attempt to sign all commits
41954533
** with gpg. When disabled, commits will be unsigned.
@@ -4217,13 +4555,14 @@
42174555
** Note: The options for timeline comments displayed on the web UI can be
42184556
** configured through the /setup_timeline web page.
42194557
*/
42204558
/*
42214559
** 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
42254564
** The crnl-glob setting is a compatibility alias.
42264565
*/
42274566
/*
42284567
** SETTING: crnl-glob width=40 versionable block-text
42294568
** This is an alias for the crlf-glob setting.
@@ -4265,22 +4604,22 @@
42654604
** The value is an external command that will launch the
42664605
** text editor command used for check-in comments.
42674606
*/
42684607
/*
42694608
** 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.
42744613
*/
42754614
/*
42764615
** 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
42824621
*/
42834622
#if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
42844623
/*
42854624
** SETTING: exec-rel-paths boolean default=on
42864625
** When executing certain external commands (e.g. diff and
@@ -4295,14 +4634,15 @@
42954634
*/
42964635
#endif
42974636
42984637
/*
42994638
** 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
43044644
** contain any globs for, e.g., images or PDFs.
43054645
*/
43064646
/*
43074647
** SETTING: forbid-delta-manifests boolean default=off
43084648
** If enabled on a client, new delta manifests are prohibited on
@@ -4340,21 +4680,21 @@
43404680
** If true, then the Fossil web server will redirect unencrypted
43414681
** login screen requests to HTTPS.
43424682
*/
43434683
/*
43444684
** 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
43504689
*/
43514690
/*
43524691
** 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
43564696
*/
43574697
/*
43584698
** SETTING: localauth boolean default=off
43594699
** If enabled, require that HTTP connections from the loopback
43604700
** address (127.0.0.1) be authenticated by password. If false,
43614701
--- 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
--- src/default.css
+++ src/default.css
@@ -732,11 +732,16 @@
732732
pre.textPlain {
733733
white-space: pre-wrap;
734734
word-wrap: break-word;
735735
}
736736
.statistics-report-graph-line {
737
+ border: 2px solid #446979;
737738
background-color: #446979;
739
+}
740
+.statistics-report-graph-extra {
741
+ border: 2px dashed #446979;
742
+ border-left-style: none;
738743
}
739744
.statistics-report-table-events th {
740745
padding: 0 1em 0 1em;
741746
}
742747
.statistics-report-table-events td {
743748
--- 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
--- src/default.css
+++ src/default.css
@@ -732,11 +732,16 @@
732732
pre.textPlain {
733733
white-space: pre-wrap;
734734
word-wrap: break-word;
735735
}
736736
.statistics-report-graph-line {
737
+ border: 2px solid #446979;
737738
background-color: #446979;
739
+}
740
+.statistics-report-graph-extra {
741
+ border: 2px dashed #446979;
742
+ border-left-style: none;
738743
}
739744
.statistics-report-table-events th {
740745
padding: 0 1em 0 1em;
741746
}
742747
.statistics-report-table-events td {
743748
--- 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
--- src/descendants.c
+++ src/descendants.c
@@ -627,11 +627,11 @@
627627
if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
628628
if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
629629
if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
630630
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
631631
db_finalize(&q);
632
- @ <br />
632
+ @ <br>
633633
style_finish_page();
634634
}
635635
636636
#if INTERFACE
637637
/* Flag parameters to compute_uses_file() */
638638
--- 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 @@
36363636
@ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
36373637
@ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
36383638
@ </span>
36393639
}
36403640
@ </ol>
3641
- @ <hr />
3641
+ @ <hr>
36423642
@ </div>
36433643
36443644
if( !ann.bMoreToDo ){
36453645
assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */
36463646
@ <h2>Origin for each line in
36473647
--- 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 @@
550550
** Display help for all commands based on provided flags.
551551
*/
552552
static void display_all_help(int mask, int useHtml, int rawOut){
553553
int i;
554554
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*/
556556
if( useHtml ) fossil_print("<!--\n");
557557
fossil_print("Help text for:\n");
558558
if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n");
559559
if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n");
560560
if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n");
@@ -859,11 +859,11 @@
859859
}
860860
}
861861
}else{
862862
int i;
863863
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 */
865865
style_header("Help");
866866
867867
@ <a name='commands'></a>
868868
@ <h1>Available commands:</h1>
869869
@ <div class="columns" style="column-width: 12ex;">
@@ -966,11 +966,11 @@
966966
** Show all help text on a single page. Useful for proof-reading.
967967
*/
968968
void test_all_help_page(void){
969969
int i;
970970
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*/
972972
Blob buf;
973973
blob_init(&buf,0,0);
974974
style_set_current_feature("test");
975975
style_header("All Help Text");
976976
@ <dl>
977977
--- 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 @@
212212
}
213213
blob_init(&comment, pTNote->zComment, -1);
214214
wiki_convert(&comment, 0, WIKI_INLINE);
215215
blob_reset(&comment);
216216
@ </div>
217
- @ </blockquote><hr />
217
+ @ </blockquote><hr>
218218
}
219219
220220
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
221221
wiki_convert(&fullbody, 0, 0);
222222
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
@@ -228,11 +228,11 @@
228228
}
229229
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
230230
" FROM tag"
231231
" WHERE tagname GLOB 'event-%q*'",
232232
zId);
233
- attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
233
+ attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>");
234234
document_emit_js();
235235
style_finish_page();
236236
manifest_destroy(pTNote);
237237
}
238238
@@ -513,26 +513,26 @@
513513
@ <blockquote>
514514
blob_init(&event, 0, 0);
515515
blob_append(&event, zBody, -1);
516516
safe_html_context(DOCSRC_WIKI);
517517
wiki_render_by_mimetype(&event, zMimetype);
518
- @ </blockquote><hr />
518
+ @ </blockquote><hr>
519519
blob_reset(&event);
520520
}
521521
for(n=2, z=zBody; z[0]; z++){
522522
if( z[0]=='\n' ) n++;
523523
}
524524
if( n<20 ) n = 20;
525525
if( n>40 ) n = 40;
526526
@ <form method="post" action="%R/technoteedit"><div>
527527
login_insert_csrf_secret();
528
- @ <input type="hidden" name="name" value="%h(zId)" />
528
+ @ <input type="hidden" name="name" value="%h(zId)">
529529
@ <table border="0" cellspacing="10">
530530
531531
@ <tr><th align="right" valign="top">Timestamp (UTC):</th>
532532
@ <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)">
534534
@ </td></tr>
535535
536536
@ <tr><th align="right" valign="top">Timeline Comment:</th>
537537
@ <td valign="top">
538538
@ <textarea name="c" class="technoteedit" cols="80"
@@ -539,18 +539,18 @@
539539
@ rows="3" wrap="virtual">%h(zComment)</textarea>
540540
@ </td></tr>
541541
542542
@ <tr><th align="right" valign="top">Timeline Background Color:</th>
543543
@ <td valign="top">
544
- @ <input type='checkbox' name='newclr'%s(zClrFlag) />
544
+ @ <input type='checkbox' name='newclr'%s(zClrFlag)>
545545
@ Use custom color: \
546546
@ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
547547
@ </td></tr>
548548
549549
@ <tr><th align="right" valign="top">Tags:</th>
550550
@ <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)">
552552
@ </td></tr>
553553
554554
@ <tr><th align="right" valign="top">\
555555
@ %z(href("%R/markup_help"))Markup Style</a>:</th>
556556
@ <td valign="top">
@@ -562,14 +562,14 @@
562562
@ <textarea name="w" class="technoteedit" cols="80"
563563
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
564564
@ </td></tr>
565565
566566
@ <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">
569569
if( P("preview") ){
570
- @ <input type="submit" name="submit" value="Submit" />
570
+ @ <input type="submit" name="submit" value="Submit">
571571
}
572572
@ </td></tr></table>
573573
@ </div></form>
574574
style_finish_page();
575575
}
576576
--- 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 @@
212212
}
213213
blob_init(&comment, pTNote->zComment, -1);
214214
wiki_convert(&comment, 0, WIKI_INLINE);
215215
blob_reset(&comment);
216216
@ </div>
217
- @ </blockquote><hr />
217
+ @ </blockquote><hr>
218218
}
219219
220220
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
221221
wiki_convert(&fullbody, 0, 0);
222222
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
@@ -228,11 +228,11 @@
228228
}
229229
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
230230
" FROM tag"
231231
" WHERE tagname GLOB 'event-%q*'",
232232
zId);
233
- attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
233
+ attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>");
234234
document_emit_js();
235235
style_finish_page();
236236
manifest_destroy(pTNote);
237237
}
238238
@@ -513,26 +513,26 @@
513513
@ <blockquote>
514514
blob_init(&event, 0, 0);
515515
blob_append(&event, zBody, -1);
516516
safe_html_context(DOCSRC_WIKI);
517517
wiki_render_by_mimetype(&event, zMimetype);
518
- @ </blockquote><hr />
518
+ @ </blockquote><hr>
519519
blob_reset(&event);
520520
}
521521
for(n=2, z=zBody; z[0]; z++){
522522
if( z[0]=='\n' ) n++;
523523
}
524524
if( n<20 ) n = 20;
525525
if( n>40 ) n = 40;
526526
@ <form method="post" action="%R/technoteedit"><div>
527527
login_insert_csrf_secret();
528
- @ <input type="hidden" name="name" value="%h(zId)" />
528
+ @ <input type="hidden" name="name" value="%h(zId)">
529529
@ <table border="0" cellspacing="10">
530530
531531
@ <tr><th align="right" valign="top">Timestamp (UTC):</th>
532532
@ <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)">
534534
@ </td></tr>
535535
536536
@ <tr><th align="right" valign="top">Timeline Comment:</th>
537537
@ <td valign="top">
538538
@ <textarea name="c" class="technoteedit" cols="80"
@@ -539,18 +539,18 @@
539539
@ rows="3" wrap="virtual">%h(zComment)</textarea>
540540
@ </td></tr>
541541
542542
@ <tr><th align="right" valign="top">Timeline Background Color:</th>
543543
@ <td valign="top">
544
- @ <input type='checkbox' name='newclr'%s(zClrFlag) />
544
+ @ <input type='checkbox' name='newclr'%s(zClrFlag)>
545545
@ Use custom color: \
546546
@ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
547547
@ </td></tr>
548548
549549
@ <tr><th align="right" valign="top">Tags:</th>
550550
@ <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)">
552552
@ </td></tr>
553553
554554
@ <tr><th align="right" valign="top">\
555555
@ %z(href("%R/markup_help"))Markup Style</a>:</th>
556556
@ <td valign="top">
@@ -562,14 +562,14 @@
562562
@ <textarea name="w" class="technoteedit" cols="80"
563563
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
564564
@ </td></tr>
565565
566566
@ <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">
569569
if( P("preview") ){
570
- @ <input type="submit" name="submit" value="Submit" />
570
+ @ <input type="submit" name="submit" value="Submit">
571571
}
572572
@ </td></tr></table>
573573
@ </div></form>
574574
style_finish_page();
575575
}
576576
--- 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
--- src/extcgi.c
+++ src/extcgi.c
@@ -49,10 +49,11 @@
4949
"FOSSIL_USER",
5050
"GATEWAY_INTERFACE",
5151
"HTTPS",
5252
"HTTP_ACCEPT",
5353
/* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */
54
+ "HTTP_ACCEPT_LANGUAGE",
5455
"HTTP_COOKIE",
5556
"HTTP_HOST",
5657
"HTTP_IF_MODIFIED_SINCE",
5758
"HTTP_IF_NONE_MATCH",
5859
"HTTP_REFERER",
5960
--- 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 @@
22772277
int i;
22782278
for(i=2; i<g.argc; i++){
22792279
fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
22802280
}
22812281
}
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
+}
22822325
22832326
/*
22842327
** Remove surplus "/" characters from the beginning of a full pathname.
22852328
** Extra leading "/" characters are benign on unix. But on Windows
22862329
** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
22872330
--- 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 @@
767767
@ </span></span>
768768
}
769769
if( fDebug & FINFO_DEBUG_MLINK ){
770770
int ii;
771771
char *zAncLink;
772
- @ <br />fid=%d(frid) \
772
+ @ <br>fid=%d(frid) \
773773
@ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \
774774
@ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \
775775
@ pfnid=%d(pfnid) mxfnid=%d(mxfnid)
776776
if( nParent>0 ){
777777
@ parents=%lld(aParent[0])
@@ -930,11 +930,11 @@
930930
mid
931931
);
932932
@ <h1>MLINK table for check-in %h(zCI)</h1>
933933
render_checkin_context(mid, 0, 1, 0);
934934
style_table_sorter();
935
- @ <hr />
935
+ @ <hr>
936936
@ <div class='brlist'>
937937
@ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
938938
@ <thead><tr>
939939
@ <th>File</th>
940940
@ <th>Parent<br>Check-in</th>
941941
--- 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 @@
563563
}
564564
fossil_print("\n");
565565
}
566566
forumthread_delete(pThread);
567567
}
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
+}
568621
569622
/*
570623
** Render a forum post for display
571624
*/
572625
void forum_render(
@@ -1178,10 +1231,13 @@
11781231
style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName,
11791232
bUnf ? "&unf" : "", bHist ? "&hist" : "");
11801233
}
11811234
style_submenu_checkbox("unf", "Unformatted", 0, 0);
11821235
style_submenu_checkbox("hist", "History", 0, 0);
1236
+ if( g.perm.Admin ){
1237
+ style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName);
1238
+ }
11831239
11841240
/* Display the thread. */
11851241
if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
11861242
forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist);
11871243
@@ -1209,10 +1265,25 @@
12091265
static int whitespace_only(const char *z){
12101266
if( z==0 ) return 1;
12111267
while( z[0] && fossil_isspace(z[0]) ){ z++; }
12121268
return z[0]==0;
12131269
}
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
+}
12141285
12151286
/*
12161287
** Add a new Forum Post artifact to the repository.
12171288
**
12181289
** Return true if a redirect occurs.
@@ -1221,11 +1292,12 @@
12211292
const char *zTitle, /* Title. NULL for replies */
12221293
int iInReplyTo, /* Post replying to. 0 for new threads */
12231294
int iEdit, /* Post being edited, or zero for a new post */
12241295
const char *zUser, /* Username. NULL means use login name */
12251296
const char *zMimetype, /* Mimetype of content. */
1226
- const char *zContent /* Content */
1297
+ const char *zContent, /* Content */
1298
+ int iFlags /* FPOST_xyz flag values */
12271299
){
12281300
char *zDate;
12291301
char *zI;
12301302
char *zG;
12311303
int iBasis;
@@ -1308,13 +1380,18 @@
13081380
@ <pre>%h(blob_str(&x))</pre>
13091381
@ </div>
13101382
blob_reset(&x);
13111383
return 0;
13121384
}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());
13151388
blob_reset(&x);
1389
+ if( (iFlags & FPOST_NO_ALERT)!=0 ){
1390
+ alert_unqueue('f', nrid);
1391
+ }
1392
+ db_commit_transaction();
13161393
cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid));
13171394
return 1;
13181395
}
13191396
}
13201397
@@ -1439,10 +1516,27 @@
14391516
@ From: anonymous<br>
14401517
}else{
14411518
@ From: %h(login_name())<br>
14421519
}
14431520
}
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
+}
14441538
14451539
/*
14461540
** WEBPAGE: forume1
14471541
**
14481542
** Start a new forum thread.
@@ -1449,17 +1543,19 @@
14491543
*/
14501544
void forumnew_page(void){
14511545
const char *zTitle = PDT("title","");
14521546
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
14531547
const char *zContent = PDT("content","");
1548
+
14541549
login_check_credentials();
14551550
if( !g.perm.WrForum ){
14561551
login_needed(g.anon.WrForum);
14571552
return;
14581553
}
14591554
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;
14611557
}
14621558
if( P("preview") && !whitespace_only(zContent) ){
14631559
@ <h1>Preview:</h1>
14641560
forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
14651561
}
@@ -1473,22 +1569,11 @@
14731569
if( P("preview") && !whitespace_only(zContent) ){
14741570
@ <input type="submit" name="submit" value="Submit">
14751571
}else{
14761572
@ <input type="submit" name="submit" value="Submit" disabled>
14771573
}
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();
14901575
@ </form>
14911576
forum_emit_js();
14921577
style_finish_page();
14931578
}
14941579
@@ -1581,13 +1666,15 @@
15811666
&& (!whitespace_only(zContent) || isDelete)
15821667
){
15831668
int done = 1;
15841669
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
15851670
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());
15871673
}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());
15891676
}else{
15901677
webpage_error("Missing 'reply' query parameter");
15911678
}
15921679
if( done ) return;
15931680
}
@@ -1671,21 +1758,11 @@
16711758
if( (bPreview && !whitespace_only(zContent)) || isDelete ){
16721759
if( !iClosed || g.perm.Admin ) {
16731760
@ <input type="submit" name="submit" value="Submit">
16741761
}
16751762
}
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();
16871764
@ </form>
16881765
forum_emit_js();
16891766
forumpost_emit_closed_state(fpid, iClosed);
16901767
style_finish_page();
16911768
}
16921769
--- 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 @@
563563
}
564564
fossil_print("\n");
565565
}
566566
forumthread_delete(pThread);
567567
}
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
+}
568621
569622
/*
570623
** Render a forum post for display
571624
*/
572625
void forum_render(
@@ -1178,10 +1231,13 @@
11781231
style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName,
11791232
bUnf ? "&unf" : "", bHist ? "&hist" : "");
11801233
}
11811234
style_submenu_checkbox("unf", "Unformatted", 0, 0);
11821235
style_submenu_checkbox("hist", "History", 0, 0);
1236
+ if( g.perm.Admin ){
1237
+ style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName);
1238
+ }
11831239
11841240
/* Display the thread. */
11851241
if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
11861242
forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist);
11871243
@@ -1209,10 +1265,25 @@
12091265
static int whitespace_only(const char *z){
12101266
if( z==0 ) return 1;
12111267
while( z[0] && fossil_isspace(z[0]) ){ z++; }
12121268
return z[0]==0;
12131269
}
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
+}
12141285
12151286
/*
12161287
** Add a new Forum Post artifact to the repository.
12171288
**
12181289
** Return true if a redirect occurs.
@@ -1221,11 +1292,12 @@
12211292
const char *zTitle, /* Title. NULL for replies */
12221293
int iInReplyTo, /* Post replying to. 0 for new threads */
12231294
int iEdit, /* Post being edited, or zero for a new post */
12241295
const char *zUser, /* Username. NULL means use login name */
12251296
const char *zMimetype, /* Mimetype of content. */
1226
- const char *zContent /* Content */
1297
+ const char *zContent, /* Content */
1298
+ int iFlags /* FPOST_xyz flag values */
12271299
){
12281300
char *zDate;
12291301
char *zI;
12301302
char *zG;
12311303
int iBasis;
@@ -1308,13 +1380,18 @@
13081380
@ <pre>%h(blob_str(&x))</pre>
13091381
@ </div>
13101382
blob_reset(&x);
13111383
return 0;
13121384
}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());
13151388
blob_reset(&x);
1389
+ if( (iFlags & FPOST_NO_ALERT)!=0 ){
1390
+ alert_unqueue('f', nrid);
1391
+ }
1392
+ db_commit_transaction();
13161393
cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid));
13171394
return 1;
13181395
}
13191396
}
13201397
@@ -1439,10 +1516,27 @@
14391516
@ From: anonymous<br>
14401517
}else{
14411518
@ From: %h(login_name())<br>
14421519
}
14431520
}
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
+}
14441538
14451539
/*
14461540
** WEBPAGE: forume1
14471541
**
14481542
** Start a new forum thread.
@@ -1449,17 +1543,19 @@
14491543
*/
14501544
void forumnew_page(void){
14511545
const char *zTitle = PDT("title","");
14521546
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
14531547
const char *zContent = PDT("content","");
1548
+
14541549
login_check_credentials();
14551550
if( !g.perm.WrForum ){
14561551
login_needed(g.anon.WrForum);
14571552
return;
14581553
}
14591554
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;
14611557
}
14621558
if( P("preview") && !whitespace_only(zContent) ){
14631559
@ <h1>Preview:</h1>
14641560
forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
14651561
}
@@ -1473,22 +1569,11 @@
14731569
if( P("preview") && !whitespace_only(zContent) ){
14741570
@ <input type="submit" name="submit" value="Submit">
14751571
}else{
14761572
@ <input type="submit" name="submit" value="Submit" disabled>
14771573
}
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();
14901575
@ </form>
14911576
forum_emit_js();
14921577
style_finish_page();
14931578
}
14941579
@@ -1581,13 +1666,15 @@
15811666
&& (!whitespace_only(zContent) || isDelete)
15821667
){
15831668
int done = 1;
15841669
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
15851670
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());
15871673
}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());
15891676
}else{
15901677
webpage_error("Missing 'reply' query parameter");
15911678
}
15921679
if( done ) return;
15931680
}
@@ -1671,21 +1758,11 @@
16711758
if( (bPreview && !whitespace_only(zContent)) || isDelete ){
16721759
if( !iClosed || g.perm.Admin ) {
16731760
@ <input type="submit" name="submit" value="Submit">
16741761
}
16751762
}
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();
16871764
@ </form>
16881765
forum_emit_js();
16891766
forumpost_emit_closed_state(fpid, iClosed);
16901767
style_finish_page();
16911768
}
16921769
--- 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 @@
8989
char **azPattern; /* Array of pointers to patterns */
9090
};
9191
#endif /* INTERFACE */
9292
9393
/*
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.
9696
**
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.
99100
**
100
-** Leading and trailing spaces on unquoted glob patterns are ignored.
101
+** Leading and trailing spaces on glob patterns are ignored unless quoted.
101102
**
102103
** An empty or null pattern list results in a null glob, which will
103104
** match nothing.
104105
*/
105106
Glob *glob_create(const char *zPatternList){
@@ -126,14 +127,14 @@
126127
}else{
127128
delimiter = ',';
128129
}
129130
p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) );
130131
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 */
135136
}
136137
if( z[i]==0 ) break;
137138
z[i] = 0;
138139
z += i+1;
139140
}
140141
--- 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 @@
10401040
if( g.perm.ModWiki && modPending ){
10411041
@ <div class="section">Moderation</div>
10421042
@ <blockquote>
10431043
@ <form method="POST" action="%R/winfo/%s(zUuid)">
10441044
@ <label><input type="radio" name="modaction" value="delete">
1045
- @ Delete this change</label><br />
1045
+ @ Delete this change</label><br>
10461046
@ <label><input type="radio" name="modaction" value="approve">
1047
- @ Approve this change</label><br />
1047
+ @ Approve this change</label><br>
10481048
@ <input type="submit" value="Submit">
10491049
@ </form>
10501050
@ </blockquote>
10511051
}
10521052
@@ -1296,11 +1296,11 @@
12961296
@ are shown.</b></p>
12971297
}
12981298
if( zGlob ){
12991299
@ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
13001300
}
1301
- @<hr /><p>
1301
+ @<hr><p>
13021302
}
13031303
blob_reset(&qp);
13041304
13051305
manifest_file_rewind(pFrom);
13061306
pFileFrom = manifest_file_next(pFrom, 0);
@@ -1820,11 +1820,11 @@
18201820
if( pRe ){
18211821
@ <b>Only differences that match regular expression "%h(zRe)"
18221822
@ are shown.</b>
18231823
DCfg.pRe = pRe;
18241824
}
1825
- @ <hr />
1825
+ @ <hr>
18261826
append_diff(zV1, zV2, &DCfg);
18271827
append_diff_javascript(diffType);
18281828
style_finish_page();
18291829
}
18301830
@@ -2128,11 +2128,11 @@
21282128
blob_zero(&downloadName);
21292129
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
21302130
object_description(rid, objdescFlags, 0, &downloadName);
21312131
style_submenu_element("Download", "%R/raw/%s?at=%T",
21322132
zUuid, file_tail(blob_str(&downloadName)));
2133
- @ <hr />
2133
+ @ <hr>
21342134
content_get(rid, &content);
21352135
if( !g.isHuman ){
21362136
/* Prevent robots from running hexdump on megabyte-sized source files
21372137
** and there by eating up lots of CPU time and bandwidth. There is
21382138
** no good reason for a robot to need a hexdump. */
@@ -2654,11 +2654,11 @@
26542654
style_submenu_element("Parsed", "%R/info/%s", zUuid);
26552655
}
26562656
if( descOnly ){
26572657
style_submenu_element("Content", "%R/artifact/%s", zUuid);
26582658
}else{
2659
- @ <hr />
2659
+ @ <hr>
26602660
content_get(rid, &content);
26612661
if( renderAsWiki ){
26622662
safe_html_context(DOCSRC_FILE);
26632663
wiki_render_by_mimetype(&content, zMime);
26642664
document_emit_js();
@@ -2801,11 +2801,11 @@
28012801
}
28022802
modPending = moderation_pending_www(rid);
28032803
@ <tr><th>Ticket:</th>
28042804
@ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
28052805
if( zTktTitle ){
2806
- @<br />%h(zTktTitle)
2806
+ @<br>%h(zTktTitle)
28072807
}
28082808
@</td></tr>
28092809
@ <tr><th>User&nbsp;&amp;&nbsp;Date:</th><td>
28102810
hyperlink_to_user(pTktChng->zUser, zDate, " on ");
28112811
hyperlink_to_date(zDate, "</td></tr>");
@@ -2816,13 +2816,13 @@
28162816
if( g.perm.ModTkt && modPending ){
28172817
@ <div class="section">Moderation</div>
28182818
@ <blockquote>
28192819
@ <form method="POST" action="%R/tinfo/%s(zUuid)">
28202820
@ <label><input type="radio" name="modaction" value="delete">
2821
- @ Delete this change</label><br />
2821
+ @ Delete this change</label><br>
28222822
@ <label><input type="radio" name="modaction" value="approve">
2823
- @ Approve this change</label><br />
2823
+ @ Approve this change</label><br>
28242824
@ <input type="submit" value="Submit">
28252825
@ </form>
28262826
@ </blockquote>
28272827
}
28282828
@@ -3268,53 +3268,53 @@
32683268
if( zChngTime ){
32693269
@ <p>The timestamp on the tag used to make the changes above
32703270
@ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
32713271
}
32723272
@ </blockquote>
3273
- @ <hr />
3273
+ @ <hr>
32743274
blob_reset(&suffix);
32753275
}
32763276
@ <p>Make changes to attributes of check-in
32773277
@ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
32783278
form_begin(0, "%R/ci_edit");
32793279
login_insert_csrf_secret();
3280
- @ <div><input type="hidden" name="r" value="%s(zUuid)" />
3280
+ @ <div><input type="hidden" name="r" value="%s(zUuid)">
32813281
@ <table border="0" cellspacing="10">
32823282
32833283
@ <tr><th align="right" valign="top">User:</th>
32843284
@ <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)">
32863286
@ </td></tr>
32873287
32883288
@ <tr><th align="right" valign="top">Comment:</th>
32893289
@ <td valign="top">
32903290
@ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea>
32913291
@ </td></tr>
32923292
32933293
@ <tr><th align="right" valign="top">Check-in Time:</th>
32943294
@ <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)">
32963296
@ </td></tr>
32973297
32983298
if( zChngTime ){
32993299
@ <tr><th align="right" valign="top">Timestamp of this change:</th>
33003300
@ <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)">
33023302
@ </td></tr>
33033303
}
33043304
33053305
@ <tr><th align="right" valign="top">Background&nbsp;Color:</th>
33063306
@ <td valign="top">
3307
- @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag) />
3307
+ @ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag)>
33083308
@ Change background color: \
33093309
@ <input type='color' name='clr'\
33103310
@ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div>
33113311
@ <div><label>
33123312
if( fNewPropagateColor ){
3313
- @ <input type="checkbox" name="pclr" checked="checked" />
3313
+ @ <input type="checkbox" name="pclr" checked="checked">
33143314
}else{
3315
- @ <input type="checkbox" name="pclr" />
3315
+ @ <input type="checkbox" name="pclr">
33163316
}
33173317
@ Propagate color to descendants</label></div>
33183318
@ <div class='font-size-80'>Be aware that fixed background
33193319
@ colors will not interact well with all available skins.
33203320
@ It is recommended that Fossil be allowed to select these
@@ -3322,14 +3322,13 @@
33223322
@ preferences into account.</div>
33233323
@ </td></tr>
33243324
33253325
@ <tr><th align="right" valign="top">Tags:</th>
33263326
@ <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)>
33283328
@ 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)">
33313330
zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
33323331
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
33333332
" AND tagxref.tagid=%d", rid, TAG_BRANCH);
33343333
db_prepare(&q,
33353334
"SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
@@ -3353,15 +3352,15 @@
33533352
}else if( !isSpecialTag && zTagName &&
33543353
fossil_strcmp(&zTagName[4], zBranchName)==0){
33553354
continue;
33563355
}
33573356
sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
3358
- @ <br /><label>
3357
+ @ <br><label>
33593358
if( P(zLabel) ){
3360
- @ <input type="checkbox" name="c%d(tagid)" checked="checked" />
3359
+ @ <input type="checkbox" name="c%d(tagid)" checked="checked">
33613360
}else{
3362
- @ <input type="checkbox" name="c%d(tagid)" />
3361
+ @ <input type="checkbox" name="c%d(tagid)">
33633362
}
33643363
if( isSpecialTag ){
33653364
@ Cancel special tag <b>%h(zTagName)</b></label>
33663365
}else{
33673366
@ Cancel tag <b>%h(&zTagName[4])</b></label>
@@ -3377,35 +3376,35 @@
33773376
zNewBranch = zBranchName;
33783377
}
33793378
@ <tr><th align="right" valign="top">Branching:</th>
33803379
@ <td valign="top">
33813380
@ <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>
33843383
@ <input id="brname" type="text" style="width:15;" name="brname" \
3385
- @ value="%h(zNewBranch)" /></td></tr>
3384
+ @ value="%h(zNewBranch)"></td></tr>
33863385
if( !fHasHidden ){
33873386
@ <tr><th align="right" valign="top">Branch Hiding:</th>
33883387
@ <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)>
33903389
@ Hide branch
33913390
@ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
33923391
@ from the timeline starting from this check-in</label>
33933392
@ </td></tr>
33943393
}
33953394
if( !fHasClosed ){
33963395
if( is_a_leaf(rid) ){
33973396
@ <tr><th align="right" valign="top">Leaf Closure:</th>
33983397
@ <td valign="top">
3399
- @ <label><input type="checkbox" name="close"%s(zCloseFlag) />
3398
+ @ <label><input type="checkbox" name="close"%s(zCloseFlag)>
34003399
@ Mark this leaf as "closed" so that it no longer appears on the
34013400
@ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label>
34023401
@ </td></tr>
34033402
}else if( zBranchName ){
34043403
@ <tr><th align="right" valign="top">Branch Closure:</th>
34053404
@ <td valign="top">
3406
- @ <label><input type="checkbox" name="close"%s(zCloseFlag) />
3405
+ @ <label><input type="checkbox" name="close"%s(zCloseFlag)>
34073406
@ Mark branch
34083407
@ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span>
34093408
@ as "closed".</label>
34103409
@ </td></tr>
34113410
}
@@ -3412,14 +3411,14 @@
34123411
}
34133412
if( zBranchName ) fossil_free(zBranchName);
34143413
34153414
34163415
@ <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">
34193418
if( P("preview") ){
3420
- @ <input type="submit" name="apply" value="Apply Changes" />
3419
+ @ <input type="submit" name="apply" value="Apply Changes">
34213420
}
34223421
@ </td></tr>
34233422
@ </table>
34243423
@ </div></form>
34253424
builtin_request_js("ci_edit.js");
@@ -3478,11 +3477,11 @@
34783477
** --date DATETIME Make DATETIME the check-in time
34793478
** --bgcolor COLOR Apply COLOR to this check-in
34803479
** --branchcolor COLOR Apply and propagate COLOR to the branch
34813480
** --tag TAG Add new TAG to this check-in
34823481
** --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
34843483
** --hide Hide branch starting from this check-in
34853484
** --close Mark this "leaf" as closed
34863485
** -n|--dry-run Print control artifact, but make no changes
34873486
** --date-override DATETIME Set the change time on the control artifact
34883487
** --user-override USER Set the user name on the control artifact
34893488
--- 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&nbsp;&amp;&nbsp;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&nbsp;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&nbsp;&amp;&nbsp;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&nbsp;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 @@
6565
6666
style_set_current_feature("test");
6767
style_header("Server Overload");
6868
@ <h2>The server load is currently too high.
6969
@ Please try again later.</h2>
70
- @ <p>Current load average: %f(load_average()).<br />
70
+ @ <p>Current load average: %f(load_average()).<br>
7171
@ Load average limit: %f(mxLoad)</p>
7272
style_finish_page();
7373
cgi_set_status(503,"Server Overload");
7474
cgi_reply();
7575
exit(0);
7676
--- 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 @@
737737
form_begin(0, "https:%s/login", g.zBaseURL+5);
738738
}else{
739739
form_begin(0, "%R/login");
740740
}
741741
if( zGoto ){
742
- @ <input type="hidden" name="g" value="%h(zGoto)" />
742
+ @ <input type="hidden" name="g" value="%h(zGoto)">
743743
}
744744
if( anonFlag ){
745
- @ <input type="hidden" name="anon" value="1" />
745
+ @ <input type="hidden" name="anon" value="1">
746746
}
747747
if( g.zLogin ){
748748
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
749749
@ <input type="submit" name="out" value="Logout"></p>
750750
@ </form>
@@ -775,11 +775,11 @@
775775
@ size="30" value="%s(anonFlag?"anonymous":"")"></td>
776776
@ </tr>
777777
@ <tr>
778778
@ <td class="form_label" id="pswdlabel">Password:</td>
779779
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
780
- @ name="p" value="" size="30" />\
780
+ @ name="p" value="" size="30">\
781781
if( zAnonPw && !noAnon ){
782782
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
783783
}
784784
@ </td>
785785
@ </tr>
@@ -809,20 +809,20 @@
809809
if( zAnonPw && !noAnon ){
810810
const char *zDecoded = captcha_decode(uSeed);
811811
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
812812
char *zCaptcha = captcha_render(zDecoded);
813813
814
- @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
814
+ @ <p><input type="hidden" name="cs" value="%u(uSeed)">
815815
@ Visitors may enter <b>anonymous</b> as the user-ID with
816816
@ the 8-character hexadecimal password shown below:</p>
817817
@ <div class="captcha"><table class="captcha"><tr><td>\
818818
@ <pre class="captcha">
819819
@ %h(zCaptcha)
820820
@ </pre></td></tr></table>
821821
if( bAutoCaptcha ) {
822822
@ <input type="button" value="Fill out captcha" id='autofillButton' \
823
- @ data-af='%s(zDecoded)' />
823
+ @ data-af='%s(zDecoded)'>
824824
builtin_request_js("login.js");
825825
}
826826
@ </div>
827827
free(zCaptcha);
828828
}
@@ -848,16 +848,16 @@
848848
@ <tr><td class="form_label" id="oldpw">Old Password:</td>
849849
@ <td><input aria-labelledby="oldpw" type="password" name="p" \
850850
@ size="30"/></td></tr>
851851
@ <tr><td class="form_label" id="newpw">New Password:</td>
852852
@ <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>
854854
@ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
855855
@ <td><input aria-labledby="reppw" type="password" name="n2" \
856
- @ size="30" /></td></tr>
856
+ @ size="30"></td></tr>
857857
@ <tr><td></td>
858
- @ <td><input type="submit" value="Change Password" /></td></tr>
858
+ @ <td><input type="submit" value="Change Password"></td></tr>
859859
@ </table>
860860
@ </form>
861861
}
862862
}
863863
style_finish_page();
@@ -1085,16 +1085,16 @@
10851085
form_begin(0, "%R/resetpw");
10861086
@ <input type='hidden' name='name' value='%h(zName)'>
10871087
@ <table>
10881088
@ <tr><td class="form_label" id="newpw">New Password:</td>
10891089
@ <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>
10911091
@ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
10921092
@ <td><input aria-labledby="reppw" type="password" name="n2" \
1093
- @ size="30" /></td></tr>
1093
+ @ size="30"></td></tr>
10941094
@ <tr><td></td>
1095
- @ <td><input type="submit" value="Change Password" /></td></tr>
1095
+ @ <td><input type="submit" value="Change Password"></td></tr>
10961096
@ </table>
10971097
@ </form>
10981098
style_finish_page();
10991099
}
11001100
@@ -1790,11 +1790,11 @@
17901790
** logging in as anonymous.
17911791
*/
17921792
void login_anonymous_available(void){
17931793
if( !g.perm.Hyperlink && g.anon.Hyperlink ){
17941794
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>
17961796
@ Use <a href="%R/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
17971797
@ to enable hyperlinks.</p>
17981798
}
17991799
}
18001800
@@ -1801,11 +1801,11 @@
18011801
/*
18021802
** While rendering a form, call this routine to add the Anti-CSRF token
18031803
** as a hidden element of the form.
18041804
*/
18051805
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)">
18071807
}
18081808
18091809
/*
18101810
** Before using the results of a form, first call this routine to verify
18111811
** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token
@@ -2123,13 +2123,13 @@
21232123
style_header("Register");
21242124
/* Print out the registration form. */
21252125
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
21262126
form_begin(0, "%R/register");
21272127
if( P("g") ){
2128
- @ <input type="hidden" name="g" value="%h(P("g"))" />
2128
+ @ <input type="hidden" name="g" value="%h(P("g"))">
21292129
}
2130
- @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" />
2130
+ @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
21312131
@ <table class="login_out">
21322132
@ <tr>
21332133
@ <td class="form_label" align="right" id="uid">User ID:</td>
21342134
@ <td><input aria-labelledby="uid" type="text" name="u" \
21352135
@ value="%h(zUserID)" size="30"></td>
@@ -2152,11 +2152,11 @@
21522152
@ value="%h(zEAddr)" size="30"></td>
21532153
@ </tr>
21542154
if( iErrLine==3 ){
21552155
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span>
21562156
if( uid>0 && login_self_password_reset_available() ){
2157
- @ <br />
2157
+ @ <br>
21582158
@ <input type="submit" name="pwreset" \
21592159
@ value="Request Password Reset For %h(zEAddr)">
21602160
}
21612161
@ </td></tr>
21622162
}
@@ -2200,11 +2200,11 @@
22002200
@ </tr>
22012201
if( iErrLine==6 ){
22022202
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
22032203
}
22042204
@ <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>
22062206
@ </table>
22072207
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
22082208
@ %h(zCaptcha)
22092209
@ </pre>
22102210
@ Enter this 8-letter code in the "Captcha" box above.
@@ -2328,12 +2328,12 @@
23282328
23292329
style_header("Request Password Reset");
23302330
/* Print out the registration form. */
23312331
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
23322332
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">
23352335
@ <table class="login_out">
23362336
@ <tr>
23372337
@ <td class="form_label" align="right" id="emaddr">Email Address:</td>
23382338
@ <td><input aria-labelledby="emaddr" type="text" name="ea" \
23392339
@ value="%h(zEAddr)" size="30"></td>
23402340
--- 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&amp;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'>&uarr; %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'>&uarr; %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&amp;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'>&uarr; %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'>&uarr; %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 @@
2424
# include <windows.h>
2525
# include <io.h>
2626
# define isatty(h) _isatty(h)
2727
# define GETPID (int)GetCurrentProcessId
2828
#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
+
2939
#include "main.h"
3040
#include <string.h>
3141
#include <time.h>
3242
#include <fcntl.h>
3343
#include <sys/types.h>
@@ -216,13 +226,13 @@
216226
const char *zLogin; /* Login name. NULL or "" if not logged in. */
217227
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
218228
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
219229
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
220230
** SSL client identity */
221
-#if defined(_WIN32) && USE_SEE
231
+#if USE_SEE
222232
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. */
224234
#endif
225235
int useLocalauth; /* No login required if from 127.0.0.1 */
226236
int noPswd; /* Logged in without password (on 127.0.0.1) */
227237
int userUid; /* Integer user id */
228238
int isHuman; /* True if access by a human, not a spider or bot */
@@ -800,29 +810,12 @@
800810
if( find_option("utc",0,0) ) g.fTimeFormat = 1;
801811
if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
802812
if( zChdir && file_chdir(zChdir, 0) ){
803813
fossil_fatal("unable to change directories to %s", zChdir);
804814
}
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);
824817
#endif
825818
if( find_option("help",0,0)!=0 ){
826819
/* If --help is found anywhere on the command line, translate the command
827820
* to "fossil help cmdname" where "cmdname" is the first argument that
828821
* does not begin with a "-" character. If all arguments start with "-",
@@ -1270,11 +1263,12 @@
12701263
#endif
12711264
#if defined(USE_MMAN_H)
12721265
blob_append(pOut, "USE_MMAN_H\n", -1);
12731266
#endif
12741267
#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");
12761270
#endif
12771271
#if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
12781272
blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
12791273
#endif
12801274
@@ -1716,10 +1710,12 @@
17161710
#endif
17171711
/* If the repository has not been opened already, then find the
17181712
** repository based on the first element of PATH_INFO and open it.
17191713
*/
17201714
if( !g.repositoryOpen ){
1715
+ char zBuf[24];
1716
+ const char *zRepoExt = ".fossil";
17211717
char *zRepo; /* Candidate repository name */
17221718
char *zToFree = 0; /* Malloced memory that needs to be freed */
17231719
const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
17241720
const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
17251721
char *zNewScript; /* Revised SCRIPT_NAME after processing */
@@ -1737,11 +1733,11 @@
17371733
size_t nBase = strlen(zBase);
17381734
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
17391735
17401736
/* The candidate repository name is some prefix of the PATH_INFO
17411737
** with ".fossil" appended */
1742
- zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
1738
+ zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
17431739
if( g.fHttpTrace ){
17441740
@ <!-- Looking for repository named "%h(zRepo)" -->
17451741
fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
17461742
}
17471743
@@ -1795,11 +1791,10 @@
17951791
*/
17961792
zCleanRepo = file_cleanup_fullpath(zRepo);
17971793
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
17981794
szFile = file_size(zCleanRepo, ExtFILE);
17991795
if( g.fHttpTrace ){
1800
- char zBuf[24];
18011796
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
18021797
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
18031798
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
18041799
}
18051800
}
@@ -1808,11 +1803,11 @@
18081803
** and check to see if there is a file or directory with the same
18091804
** name as the raw PATH_INFO text.
18101805
*/
18111806
if( szFile<0 && i>0 ){
18121807
const char *zMimetype;
1813
- assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
1808
+ assert( file_is_repository_extension(&zRepo[j]) );
18141809
zRepo[j] = 0; /* Remove the ".fossil" suffix */
18151810
18161811
/* The PATH_INFO prefix seen so far is a valid directory.
18171812
** Continue the loop with the next element of the PATH_INFO */
18181813
if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
@@ -1833,11 +1828,11 @@
18331828
** pages.
18341829
*/
18351830
if( pFileGlob!=0
18361831
&& file_isfile(zCleanRepo, ExtFILE)
18371832
&& glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
1838
- && sqlite3_strglob("*.fossil*",zRepo)!=0
1833
+ && !file_contains_repository_extension(zRepo)
18391834
&& (zMimetype = mimetype_from_name(zRepo))!=0
18401835
&& strcmp(zMimetype, "application/x-fossil-artifact")!=0
18411836
){
18421837
Blob content;
18431838
blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
@@ -1868,10 +1863,17 @@
18681863
** string is finished. Either zRepo contains the name of the
18691864
** repository to be used, or else no repository could be found and
18701865
** some kind of error response is required.
18711866
*/
18721867
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
18731875
set_base_url(0);
18741876
if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
18751877
&& allowRepoList
18761878
&& repo_list_page() ){
18771879
/* Will return a list of repositories */
@@ -1893,10 +1895,26 @@
18931895
if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
18941896
if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
18951897
cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
18961898
zPathInfo += i;
18971899
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
18981916
db_open_repository(file_cleanup_fullpath(zRepo));
18991917
if( g.fHttpTrace ){
19001918
@ <!-- repository: "%h(zRepo)" -->
19011919
@ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
19021920
@ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
@@ -2615,11 +2633,11 @@
26152633
}
26162634
}
26172635
}
26182636
}
26192637
2620
-#if defined(_WIN32) && USE_SEE
2638
+#if USE_SEE
26212639
/*
26222640
** This function attempts to parse a string value in the following
26232641
** format:
26242642
**
26252643
** "%lu:%p:%u"
@@ -2632,17 +2650,19 @@
26322650
**
26332651
** If the specified value cannot be parsed, for any reason, a fatal
26342652
** error will be raised and the process will be terminated.
26352653
*/
26362654
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. */
26412659
){
2660
+ unsigned long processId = 0;
26422661
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;
26442664
*pnSize = (SIZE_T)nSize;
26452665
}else{
26462666
fossil_fatal("failed to parse pid key");
26472667
}
26482668
}
@@ -2656,25 +2676,26 @@
26562676
** Query parameters:
26572677
**
26582678
** usepidkey When present and available, also return the
26592679
** address and size, within this server process,
26602680
** 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.
26622683
*/
26632684
void test_pid_page(void){
26642685
login_check_credentials();
26652686
if( !g.perm.Setup ){ login_needed(0); return; }
2666
-#if defined(_WIN32) && USE_SEE
2687
+#if USE_SEE
26672688
if( P("usepidkey")!=0 ){
26682689
if( g.zPidKey ){
26692690
@ %s(g.zPidKey)
26702691
return;
26712692
}else{
26722693
const char *zSavedKey = db_get_saved_encryption_key();
26732694
size_t savedKeySize = db_get_saved_encryption_key_size();
26742695
if( zSavedKey!=0 && savedKeySize>0 ){
2675
- @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize)
2696
+ @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize)
26762697
return;
26772698
}
26782699
}
26792700
}
26802701
#endif
@@ -2770,11 +2791,11 @@
27702791
** --scgi Interpret input as SCGI rather than HTTP
27712792
** --skin LABEL Use override skin LABEL. Use an empty string ("")
27722793
** to force use of the current local skin config.
27732794
** --th-trace Trace TH1 execution (for debugging purposes)
27742795
** --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.
27762797
**
27772798
** See also: [[cgi]], [[server]], [[winsrv]]
27782799
*/
27792800
void cmd_http(void){
27802801
const char *zIpAddr = 0;
@@ -3111,11 +3132,11 @@
31113132
** --repolist If REPOSITORY is dir, URL "/" lists repos
31123133
** --scgi Accept SCGI rather than HTTP
31133134
** --skin LABEL Use override skin LABEL
31143135
** --th-trace Trace TH1 execution (for debugging purposes)
31153136
** --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.
31173138
**
31183139
** See also: [[cgi]], [[http]], [[winsrv]]
31193140
*/
31203141
void cmd_webserver(void){
31213142
int iPort, mxPort; /* Range of TCP ports allowed */
@@ -3140,10 +3161,14 @@
31403161
int findServerArg = 2; /* argv index for find_server_repository() */
31413162
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
31423163
const char *zJsMode; /* The --jsmode parameter */
31433164
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
31443165
3166
+
3167
+#if USE_SEE
3168
+ db_setup_for_saved_encryption_key();
3169
+#endif
31453170
31463171
#if defined(_WIN32)
31473172
const char *zStopperFile; /* Name of file used to terminate server */
31483173
zStopperFile = find_option("stopper", 0, 1);
31493174
#endif
@@ -3356,10 +3381,11 @@
33563381
** This has to happen ahead of the other signal() calls below.
33573382
** They apply after the HTTP hit is handled, but this one needs
33583383
** to be registered while we're waiting for that to occur.
33593384
**/
33603385
signal(SIGTERM, fossil_exit);
3386
+ signal(SIGINT, fossil_exit);
33613387
}
33623388
#endif /* !WIN32 */
33633389
33643390
/* Start up an HTTP server
33653391
*/
33663392
--- 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 @@
21152115
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
21162116
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
21172117
21182118
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
21192119
$(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 \
21212121
-sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \
21222122
-sENVIRONMENT=web \
21232123
-sMODULARIZE \
21242124
-sEXPORT_NAME=initPikchrModule \
21252125
--minify 0
21262126
--- 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
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -230,11 +230,11 @@
230230
blob_appendf(ob, "</h%d>", level);
231231
}
232232
233233
static void html_hrule(struct Blob *ob, void *opaque){
234234
INTER_BLOCK(ob);
235
- blob_append_literal(ob, "<hr />\n");
235
+ blob_append_literal(ob, "<hr>\n");
236236
}
237237
238238
239239
static void html_list(
240240
struct Blob *ob,
@@ -758,16 +758,16 @@
758758
html_quote(ob, blob_buffer(alt), blob_size(alt));
759759
if( title && blob_size(title)>0 ){
760760
blob_append_literal(ob, "\" title=\"");
761761
html_quote(ob, blob_buffer(title), blob_size(title));
762762
}
763
- blob_append_literal(ob, "\" />");
763
+ blob_append_literal(ob, "\">");
764764
return 1;
765765
}
766766
767767
static int html_linebreak(struct Blob *ob, void *opaque){
768
- blob_append_literal(ob, "<br />\n");
768
+ blob_append_literal(ob, "<br>\n");
769769
return 1;
770770
}
771771
772772
static int html_link(
773773
struct Blob *ob,
774774
--- 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 @@
762762
763763
/************************************************************************
764764
** All of the information needed to do the merge is now contained in the
765765
** FV table. Starting here, we begin to actually carry out the merge.
766766
**
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.
785768
** Copy the M content over into V.
786769
*/
787770
db_prepare(&q,
788771
"SELECT idv, ridm, fn, islinkm FROM fv"
789772
" WHERE idp>0 AND idv>0 AND idm>0"
@@ -809,14 +792,18 @@
809792
}
810793
db_finalize(&q);
811794
812795
/*
813796
** 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.
814801
*/
815802
db_prepare(&q,
816803
"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"
818805
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
819806
glob_expr("fv.fn", zBinGlob)
820807
);
821808
while( db_step(&q)==SQLITE_ROW ){
822809
int ridm = db_column_int(&q, 0);
823810
--- 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 @@
861861
** are reverted and permanently lost.
862862
** -n|--dry-run Do nothing, but print what would have happened
863863
** -v|--verbose Extra output explaining what happens
864864
**
865865
** > fossil patch diff [DIRECTORY] FILENAME
866
+** > fossil patch gdiff [DIRECTORY] FILENAME
866867
**
867868
** 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:
869871
**
870872
** -f|--force Continue trying to perform the diff even if
871873
** baseline information is missing from the current
872874
** repository
873875
**
@@ -908,11 +910,11 @@
908910
void patch_cmd(void){
909911
const char *zCmd;
910912
size_t n;
911913
if( g.argc<3 ){
912914
patch_usage:
913
- usage("apply|create|diff|pull|push|view");
915
+ usage("apply|create|diff|gdiff|pull|push|view");
914916
}
915917
zCmd = g.argv[2];
916918
n = strlen(zCmd);
917919
if( strncmp(zCmd, "apply", n)==0 ){
918920
char *zIn;
@@ -934,23 +936,23 @@
934936
verify_all_options();
935937
db_must_be_within_tree();
936938
patch_create(flags, zOut, stdout);
937939
fossil_free(zOut);
938940
}else
939
- if( strncmp(zCmd, "diff", n)==0 ){
941
+ if( (strncmp(zCmd, "diff", n)==0) || (strncmp(zCmd, "gdiff", n)==0) ){
940942
char *zIn;
941943
unsigned flags = 0;
942944
DiffConfig DCfg;
943945
944946
if( find_option("tk",0,0)!=0 ){
945947
db_close(0);
946948
diff_tk("patch diff", 3);
947949
return;
948950
}
949
- diff_options(&DCfg, zCmd[0]=='g', 0);
950951
db_find_and_open_repository(0, 0);
951952
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
953
+ diff_options(&DCfg, zCmd[0]=='g', 0);
952954
verify_all_options();
953955
zIn = patch_find_patch_filename("apply");
954956
patch_attach(zIn, stdin);
955957
patch_diff(flags, &DCfg);
956958
fossil_free(zIn);
957959
--- 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 @@
312312
db_finalize(&ins);
313313
if( n>1 ){
314314
@ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;">
315315
piechart_render(width,height, PIE_OTHER|PIE_PERCENT);
316316
@ </svg>
317
- @ <hr />
317
+ @ <hr>
318318
}
319319
@ <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>
322322
@ 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>
324324
@ <input type='submit' value='Draw The Pie Chart'/>
325325
@ </form>
326326
@ <p>Interesting test cases:
327327
@ <ul>
328328
@ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a>
329329
--- 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 @@
143143
blob_init(&base, g.zRepositoryName, -1);
144144
sqlite3_open(":memory:", &g.db);
145145
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
146146
db_multi_exec("CREATE TABLE vfile(pathname);");
147147
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
+ );
149153
allRepo = 0;
150154
}
151155
n = db_int(0, "SELECT count(*) FROM sfile");
152156
if( n==0 ){
153157
sqlite3_close(g.db);
@@ -170,17 +174,22 @@
170174
" FROM sfile ORDER BY pathname COLLATE nocase;");
171175
rNow = db_double(0, "SELECT julianday('now')");
172176
while( db_step(&q)==SQLITE_ROW ){
173177
const char *zName = db_column_text(&q, 0);
174178
int nName = (int)strlen(zName);
179
+ int nSuffix = 7; /* ".fossil" */
175180
char *zUrl;
176181
char *zAge;
177182
char *zFull;
178183
RepoInfo x;
179184
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);
182191
if( zName[0]=='/'
183192
#ifdef _WIN32
184193
|| sqlite3_strglob("[a-zA-Z]:/*", zName)==0
185194
#endif
186195
){
@@ -197,11 +206,15 @@
197206
zSkinRepo = mprintf("%s", x.zRepoName);
198207
zSkinUrl = mprintf("%s", zUrl);
199208
}
200209
}
201210
fossil_free(zFull);
202
- if( !x.isValid ){
211
+ if( !x.isValid
212
+#if USE_SEE
213
+ && !bEncrypted
214
+#endif
215
+ ){
203216
continue;
204217
}
205218
if( x.isRepolistSkin==2 && !allRepo ){
206219
/* Repositories with repolist-skin==2 are omitted from directory
207220
** scan lists, but included in "fossil all ui" lists */
@@ -218,11 +231,11 @@
218231
/* This repository has no entry in the "event" table.
219232
** Its age will still be maximum, so data-sortkey will work. */
220233
zAge = mprintf("unknown");
221234
}
222235
blob_append_sql(&html, "<tr><td valign='top'>");
223
- if( sqlite3_strglob("*.fossil", zName)!=0 ){
236
+ if( !file_ends_with_repository_extension(zName,0) ){
224237
/* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
225238
** do not work for repositories whose names do not end in ".fossil".
226239
** So do not hyperlink those cases. */
227240
blob_append_sql(&html,"%h",zName);
228241
} else if( sqlite3_strglob("*/.*", zName)==0 ){
@@ -230,21 +243,28 @@
230243
blob_append_sql(&html, "%h (hidden)", zName);
231244
} else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
232245
blob_append_sql(&html,
233246
"<a href='%R/%T/home' target='_blank'>/%h</a>\n",
234247
zUrl, zName);
235
- }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){
248
+ }else if( file_ends_with_repository_extension(zName,1) ){
236249
/* As described in
237250
** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
238251
** foo.fossil and foo/bar.fossil both exist and we create a
239252
** link to foo/bar/... then the URI dispatcher will instead
240253
** see that as a link to foo.fossil. In such cases, do not
241254
** emit a link to foo/bar.fossil. */
242255
char * zDirPart = file_dirname(zName);
243256
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
+ ) ){
246266
blob_append_sql(&html,
247267
"<s>%h</s> (directory/repo name collision)\n",
248268
zName);
249269
}else{
250270
blob_append_sql(&html,
@@ -300,11 +320,11 @@
300320
}else{
301321
/* If no repositories were found that had the "repolist_skin"
302322
** property set, then use a default skin */
303323
@ <html>
304324
@ <head>
305
- @ <base href="%s(g.zBaseURL)/" />
325
+ @ <base href="%s(g.zBaseURL)/">
306326
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
307327
@ <title>Repository List</title>
308328
@ </head>
309329
@ <body>
310330
@ <h1 align="center">Fossil Repositories</h1>
311331
--- 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 @@
5454
login_needed(g.anon.RdTkt || g.anon.NewTkt);
5555
return;
5656
}
5757
style_header("Ticket Main Menu");
5858
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);
6060
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);
6262
6363
blob_zero(&ril);
6464
ticket_init();
6565
6666
db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title");
@@ -105,11 +105,11 @@
105105
Th_Store("report_items", blob_str(&ril));
106106
107107
Th_Render(zScript);
108108
109109
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);
111111
112112
style_finish_page();
113113
}
114114
115115
/*
@@ -593,38 +593,38 @@
593593
style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
594594
if( zErr ){
595595
@ <blockquote class="reportError">%h(zErr)</blockquote>
596596
}
597597
@ <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>
602602
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
603603
@ </p>
604604
login_insert_csrf_secret();
605605
if( g.perm.Admin ){
606606
@ <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)">
608608
@ </p>
609609
@ <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:"")">
611611
@ </p>
612612
} else {
613
- @ <input type="hidden" name="w" value="%h(zOwner)" />
613
+ @ <input type="hidden" name="w" value="%h(zOwner)">
614614
if( zTag && zTag[0] ){
615
- @ <input type="hidden" name="x" value="%h(zTag)" />
615
+ @ <input type="hidden" name="x" value="%h(zTag)">
616616
}
617617
}
618618
@ <p>Enter an optional color key in the following box. (If blank, no
619619
@ color key is displayed.) Each line contains the text for a single
620620
@ 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>
622622
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
623623
@ </p>
624624
625
- @ <p>Optional human-readable description for this report<br />
625
+ @ <p>Optional human-readable description for this report<br>
626626
@ %z(href("%R/markup_help"))Markup style</a>:
627627
mimetype_option_menu(zMimetype, "m");
628628
@ <br><textarea aria-label="Description:" name="d" class="wikiedit" \
629629
@ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea>
630630
@ </p>
@@ -637,13 +637,13 @@
637637
@ </form>
638638
report_format_hints();
639639
style_finish_page();
640640
return;
641641
}
642
- @ <input type="submit" value="Apply Changes" />
642
+ @ <input type="submit" value="Apply Changes">
643643
if( rn>0 ){
644
- @ <input type="submit" value="Delete This Report" name="del1" />
644
+ @ <input type="submit" value="Delete This Report" name="del1">
645645
}
646646
@ </div></form>
647647
report_format_hints();
648648
style_finish_page();
649649
}
@@ -657,11 +657,11 @@
657657
zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'");
658658
if( zSchema==0 ){
659659
zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema"
660660
" WHERE name='ticket'");
661661
}
662
- @ <hr /><h3>TICKET Schema</h3>
662
+ @ <hr><h3>TICKET Schema</h3>
663663
@ <blockquote><pre>
664664
@ <code class="language-sql">%h(zSchema)</code>
665665
@ </pre></blockquote>
666666
@ <h3>Notes</h3>
667667
@ <ul>
668668
--- 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 @@
926926
static const char *zSnippetCall;
927927
if( srchFlags==0 ) return;
928928
sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
929929
search_rank_sqlfunc, 0, 0);
930930
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] = ' ';
932932
}
933933
blob_init(&sql, 0, 0);
934934
if( search_index_type(0)==4 ){
935935
/* If this repo is still using the legacy FTS4 search index, then
936936
** the snippet() function is slightly different */
@@ -1078,11 +1078,11 @@
10781078
nRow++;
10791079
@ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
10801080
if( fDebug ){
10811081
@ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
10821082
}
1083
- @ <br /><span class='snippet'>%z(cleanSnippet(zSnippet)) \
1083
+ @ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \
10841084
if( zDate && zDate[0] && strstr(zLabel,zDate)==0 ){
10851085
@ <small>(%h(zDate))</small>
10861086
}
10871087
@ </span></li>
10881088
if( nLimit && nRow>=nLimit ) break;
@@ -1512,11 +1512,14 @@
15121512
fossil_print("%s\n",blob_str(&out));
15131513
blob_reset(&in);
15141514
blob_reset(&out);
15151515
}
15161516
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.
15181521
*/
15191522
static const char zFtsSchema[] =
15201523
@ -- One entry for each possible search result
15211524
@ CREATE TABLE IF NOT EXISTS repository.ftsdocs(
15221525
@ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid
@@ -1542,18 +1545,100 @@
15421545
static const char zFtsDrop[] =
15431546
@ DROP TABLE IF EXISTS repository.ftsidx;
15441547
@ DROP VIEW IF EXISTS repository.ftscontent;
15451548
@ DROP TABLE IF EXISTS repository.ftsdocs;
15461549
;
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
+}
15471627
15481628
/*
15491629
** Create or drop the tables associated with a full-text index.
15501630
*/
15511631
static int searchIdxExists = -1;
15521632
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
+ }
15551640
search_sql_setup(g.db);
15561641
db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/);
15571642
searchIdxExists = 1;
15581643
}
15591644
void search_drop_index(void){
@@ -1894,12 +1979,14 @@
18941979
** enable cdtwe Enable various kinds of search. c=Check-ins,
18951980
** d=Documents, t=Tickets, w=Wiki, e=Tech Notes.
18961981
**
18971982
** disable cdtwe Disable various kinds of search
18981983
**
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.
19011988
**
19021989
** The current search settings are displayed after any changes are applied.
19031990
** Run this command with no arguments to simply see the settings.
19041991
*/
19051992
void fts_config_cmd(void){
@@ -1909,11 +1996,11 @@
19091996
} aCmd[] = {
19101997
{ 1, "reindex" },
19111998
{ 2, "index" },
19121999
{ 3, "disable" },
19132000
{ 4, "enable" },
1914
- { 5, "stemmer" },
2001
+ { 5, "tokenizer"},
19152002
};
19162003
static const struct {
19172004
const char *zSetting;
19182005
const char *zName;
19192006
const char *zSw;
@@ -1966,16 +2053,23 @@
19662053
for(j=0; j<count(aSetng); j++){
19672054
if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
19682055
db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
19692056
}
19702057
}
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
+ }
19712070
}
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
-
19772071
19782072
/* destroy or rebuild the index, if requested */
19792073
if( iAction>=1 ){
19802074
search_drop_index();
19812075
}
@@ -1986,12 +2080,12 @@
19862080
/* Always show the status before ending */
19872081
for(i=0; i<count(aSetng); i++){
19882082
fossil_print("%-17s %s\n", aSetng[i].zName,
19892083
db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
19902084
}
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));
19932087
if( search_index_exists() ){
19942088
fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1));
19952089
fossil_print("%-17s %d\n", "documents:",
19962090
db_int(0, "SELECT count(*) FROM ftsdocs"));
19972091
}else{
19982092
--- 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 @@
219219
@ checked="checked" \
220220
}
221221
if( disabled ){
222222
@ disabled="disabled" \
223223
}
224
- @ /> <b>%s(zLabel)</b></label>
224
+ @ > <b>%s(zLabel)</b></label>
225225
}
226226
227227
/*
228228
** Generate an entry box for an attribute.
229229
*/
@@ -251,11 +251,11 @@
251251
@ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \
252252
@ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
253253
if( disabled ){
254254
@ disabled="disabled" \
255255
}
256
- @ /> <b>%s(zLabel)</b>
256
+ @ > <b>%s(zLabel)</b>
257257
}
258258
259259
/*
260260
** Generate a text box for an attribute.
261261
*/
@@ -371,11 +371,11 @@
371371
@
372372
@ <p>Additional parameters that control the behavior of Javascript:</p>
373373
@ <blockquote>
374374
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
375375
"auto-hyperlink-delay", "ah-delay", "50", 0);
376
- @ <br />
376
+ @ <br>
377377
onoff_attribute("Also require a mouse event before enabling hyperlinks",
378378
"auto-hyperlink-mouseover", "ahmo", 0, 0);
379379
@ </blockquote>
380380
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
381381
@ and "require a mouse event" should be turned on. These values only come
@@ -412,15 +412,15 @@
412412
@ <p>The settings on this page are intended to help site administrators
413413
@ defend the site against robots.
414414
@
415415
@ <form action="%R/setup_robot" method="post"><div>
416416
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>
419419
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>
422422
@ </div></form>
423423
db_end_transaction(0);
424424
style_finish_page();
425425
}
426426
@@ -444,12 +444,12 @@
444444
style_set_current_feature("setup");
445445
style_header("Access Control Settings");
446446
db_begin_transaction();
447447
@ <form action="%R/setup_access" method="post"><div>
448448
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>
451451
multiple_choice_attribute("Redirect to HTTPS",
452452
"redirect-to-https", "redirhttps", "0",
453453
count(azRedirectOpts)/2, azRedirectOpts);
454454
@ <p>Force the use of HTTPS by redirecting to HTTPS when an
455455
@ unencrypted request is received. This feature can be enabled
@@ -457,11 +457,11 @@
457457
@ <p>Further details: When enabled, this option causes the $secureurl TH1
458458
@ variable is set to an "https:" variant of $baseurl. Otherwise,
459459
@ $secureurl is just an alias for $baseurl.
460460
@ (Property: "redirect-to-https". "0" for off, "1" for Login page only,
461461
@ "2" otherwise.)
462
- @ <hr />
462
+ @ <hr>
463463
onoff_attribute("Require password for local access",
464464
"localauth", "localauth", 0, 0);
465465
@ <p>When enabled, the password sign-in is always required for
466466
@ web access. When disabled, unrestricted web access from 127.0.0.1
467467
@ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
@@ -483,64 +483,64 @@
483483
@ <li> The server is started from CGI without the "localauth" keyword
484484
@ in the CGI script.
485485
@ </ol>
486486
@ (Property: "localauth")
487487
@
488
- @ <hr />
488
+ @ <hr>
489489
onoff_attribute("Enable /test_env",
490490
"test_env_enable", "test_env_enable", 0, 0);
491491
@ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
492492
@ users. When disabled (the default) only users Admin and Setup can visit
493493
@ the /test_env page.
494494
@ (Property: "test_env_enable")
495495
@ </p>
496496
@
497
- @ <hr />
497
+ @ <hr>
498498
onoff_attribute("Enable /artifact_stats",
499499
"artifact_stats_enable", "artifact_stats_enable", 0, 0);
500500
@ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all
501501
@ users. When disabled (the default) only users with check-in privilege may
502502
@ access the /artifact_stats page.
503503
@ (Property: "artifact_stats_enable")
504504
@ </p>
505505
@
506
- @ <hr />
506
+ @ <hr>
507507
onoff_attribute("Allow REMOTE_USER authentication",
508508
"remote_user_ok", "remote_user_ok", 0, 0);
509509
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
510510
@ login name of a valid user and no other login credentials are available,
511511
@ then the REMOTE_USER is accepted as an authenticated user.
512512
@ (Property: "remote_user_ok")
513513
@ </p>
514514
@
515
- @ <hr />
515
+ @ <hr>
516516
onoff_attribute("Allow HTTP_AUTHENTICATION authentication",
517517
"http_authentication_ok", "http_authentication_ok", 0, 0);
518518
@ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment
519519
@ variable or the "Authentication:" HTTP header to find the username and
520520
@ password. This is another way of supporting Basic Authentication.
521521
@ (Property: "http_authentication_ok")
522522
@ </p>
523523
@
524
- @ <hr />
524
+ @ <hr>
525525
entry_attribute("Login expiration time", 6, "cookie-expire", "cex",
526526
"8766", 0);
527527
@ <p>The number of hours for which a login is valid. This must be a
528528
@ positive number. The default is 8766 hours which is approximately equal
529529
@ to a year.
530530
@ (Property: "cookie-expire")</p>
531531
532
- @ <hr />
532
+ @ <hr>
533533
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
534534
"5000000", 0);
535535
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
536536
@ to this many bytes, uncompressed. If the client requires more data
537537
@ than this, then the client will issue multiple HTTP requests.
538538
@ Values below 1 million are not recommended. 5 million is a
539539
@ reasonable number. (Property: "max-download")</p>
540540
541
- @ <hr />
541
+ @ <hr>
542542
entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
543543
"30", 0);
544544
545545
@ <p>Fossil tries to spend less than this many seconds gathering
546546
@ the out-bound data of sync, clone, and pull packets.
@@ -547,11 +547,11 @@
547547
@ If the client request takes longer, a partial reply is given similar
548548
@ to the download packet limit. 30s is a reasonable default.
549549
@ (Property: "max-download-time")</p>
550550
551551
@ <a id="slal"></a>
552
- @ <hr />
552
+ @ <hr>
553553
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
554554
"0.0", 0);
555555
@ <p>Some expensive operations (such as computing tarballs, zip archives,
556556
@ or annotation/blame pages) are prohibited if the load average on the host
557557
@ computer is too large. Set the threshold for disallowing expensive
@@ -562,21 +562,21 @@
562562
@ (Property: "max-loadavg")</p>
563563
564564
/* Add the auto-hyperlink settings controls. These same controls
565565
** are also accessible from the /setup_robot page.
566566
*/
567
- @ <hr />
567
+ @ <hr>
568568
addAutoHyperlinkSettings();
569569
570
- @ <hr />
570
+ @ <hr>
571571
onoff_attribute("Require a CAPTCHA if not logged in",
572572
"require-captcha", "reqcapt", 1, 0);
573573
@ <p>Require a CAPTCHA for edit operations (appending, creating, or
574574
@ editing wiki or tickets or adding attachments to wiki or tickets)
575575
@ for users who are not logged in. (Property: "require-captcha")</p>
576576
577
- @ <hr />
577
+ @ <hr>
578578
entry_attribute("Public pages", 30, "public-pages",
579579
"pubpage", "", 0);
580580
@ <p>A comma-separated list of glob patterns for pages that are accessible
581581
@ without needing a login and using the privileges given by the
582582
@ "Default privileges" setting below.
@@ -587,57 +587,57 @@
587587
@ latest version of the embedded documentation in the www/ folder without
588588
@ allowing them to see the rest of the source code.
589589
@ (Property: "public-pages")
590590
@ </p>
591591
592
- @ <hr />
592
+ @ <hr>
593593
onoff_attribute("Allow users to register themselves",
594594
"self-register", "selfreg", 0, 0);
595595
@ <p>Allow users to register themselves on the /register webpage.
596596
@ A self-registration creates a new entry in the USER table and
597597
@ perhaps also in the SUBSCRIBER table if email notification is
598598
@ enabled.
599599
@ (Property: "self-register")</p>
600600
601
- @ <hr />
601
+ @ <hr>
602602
onoff_attribute("Allow users to reset their own passwords",
603603
"self-pw-reset", "selfpw", 0, 0);
604604
@ <p>Allow users to request that an email contains a hyperlink to a
605605
@ password reset page be sent to their email address of record. This
606606
@ enables forgetful users to recover their forgotten passwords without
607607
@ administrator intervention.
608608
@ (Property: "self-pw-reset")</p>
609609
610
- @ <hr />
610
+ @ <hr>
611611
onoff_attribute("Email verification required for self-registration",
612612
"selfreg-verify", "sfverify", 0, 0);
613613
@ <p>If enabled, self-registration creates a new entry in the USER table
614614
@ with only capabilities "7". The default user capabilities are not
615615
@ added until the email address associated with the self-registration
616616
@ has been verified. This setting only makes sense if
617617
@ email notifications are enabled.
618618
@ (Property: "selfreg-verify")</p>
619619
620
- @ <hr />
620
+ @ <hr>
621621
onoff_attribute("Allow anonymous subscriptions",
622622
"anon-subscribe", "anonsub", 1, 0);
623623
@ <p>If disabled, email notification subscriptions are only allowed
624624
@ for users with a login. If Nobody or Anonymous visit the /subscribe
625625
@ page, they are redirected to /register or /login.
626626
@ (Property: "anon-subscribe")</p>
627627
628
- @ <hr />
628
+ @ <hr>
629629
entry_attribute("Authorized subscription email addresses", 35,
630630
"auth-sub-email", "asemail", "", 0);
631631
@ <p>This is a comma-separated list of GLOB patterns that specify
632632
@ email addresses that are authorized to subscriptions. If blank
633633
@ (the usual case), then any email address can be used to self-register.
634634
@ This setting is used to limit subscriptions to members of a particular
635635
@ organization or group based on their email address.
636636
@ (Property: "auth-sub-email")</p>
637637
638
- @ <hr />
638
+ @ <hr>
639639
entry_attribute("Default privileges", 10, "default-perms",
640640
"defaultperms", "u", 0);
641641
@ <p>Permissions given to users that... <ul><li>register themselves using
642642
@ the self-registration procedure (if enabled), or <li>access "public"
643643
@ pages identified by the public-pages glob pattern above, or <li>
@@ -645,21 +645,21 @@
645645
@ <p>Recommended value: "u" for Reader.
646646
@ <a href="%R/setup_ucap_list">Capability Key</a>.
647647
@ (Property: "default-perms")
648648
@ </p>
649649
650
- @ <hr />
650
+ @ <hr>
651651
onoff_attribute("Show javascript button to fill in CAPTCHA",
652652
"auto-captcha", "autocaptcha", 0, 0);
653653
@ <p>When enabled, a button appears on the login screen for user
654654
@ "anonymous" that will automatically fill in the CAPTCHA password.
655655
@ This is less secure than forcing the user to do it manually, but is
656656
@ probably secure enough and it is certainly more convenient for
657657
@ anonymous users. (Property: "auto-captcha")</p>
658658
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>
661661
@ </div></form>
662662
db_end_transaction(0);
663663
style_finish_page();
664664
}
665665
@@ -762,11 +762,11 @@
762762
@ <p><form action="%R/setup_login_group" method="post"><div>
763763
login_insert_csrf_secret();
764764
@ To leave this login group press
765765
@ <input type="submit" value="Leave Login Group" name="leave">
766766
@ </form></p>
767
- @ <hr /><h2>Implementation Details</h2>
767
+ @ <hr><h2>Implementation Details</h2>
768768
@ <p>The following are fields from the CONFIG table related to login-groups,
769769
@ provided here for instructional and debugging purposes:</p>
770770
@ <table border='1' class='sortable' data-column-types='ttt' \
771771
@ data-init-sort='1'>
772772
@ <thead><tr>
@@ -814,42 +814,42 @@
814814
style_set_current_feature("setup");
815815
style_header("Timeline Display Preferences");
816816
db_begin_transaction();
817817
@ <form action="%R/setup_timeline" method="post"><div>
818818
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>
820820
821
- @ <hr />
821
+ @ <hr>
822822
onoff_attribute("Allow block-markup in timeline",
823823
"timeline-block-markup", "tbm", 0, 0);
824824
@ <p>In timeline displays, check-in comments can be displayed with or
825825
@ without block markup such as paragraphs, tables, etc.
826826
@ (Property: "timeline-block-markup")</p>
827827
828
- @ <hr />
828
+ @ <hr>
829829
onoff_attribute("Plaintext comments on timelines",
830830
"timeline-plaintext", "tpt", 0, 0);
831831
@ <p>In timeline displays, check-in comments are displayed literally,
832832
@ without any wiki or HTML interpretation. Use CSS to change
833833
@ display formatting features such as fonts and line-wrapping behavior.
834834
@ (Property: "timeline-plaintext")</p>
835835
836
- @ <hr />
836
+ @ <hr>
837837
onoff_attribute("Truncate comment at first blank line (Git-style)",
838838
"timeline-truncate-at-blank", "ttb", 0, 0);
839839
@ <p>In timeline displays, check-in comments are displayed only through
840840
@ the first blank line. This is the traditional way to display comments
841841
@ in Git repositories (Property: "timeline-truncate-at-blank")</p>
842842
843
- @ <hr />
843
+ @ <hr>
844844
onoff_attribute("Break comments at newline characters",
845845
"timeline-hard-newlines", "thnl", 0, 0);
846846
@ <p>In timeline displays, newline characters in check-in comments force
847847
@ a line break on the display.
848848
@ (Property: "timeline-hard-newlines")</p>
849849
850
- @ <hr />
850
+ @ <hr>
851851
onoff_attribute("Use Universal Coordinated Time (UTC)",
852852
"timeline-utc", "utc", 1, 0);
853853
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
854854
@ Zulu) instead of in local time. On this server, local time is currently
855855
tmDiff = db_double(0.0, "SELECT julianday('now')");
@@ -866,40 +866,40 @@
866866
}else{
867867
@ %s(zTmDiff) hours ahead of UTC.</p>
868868
}
869869
@ <p>(Property: "timeline-utc")
870870
871
- @ <hr />
871
+ @ <hr>
872872
multiple_choice_attribute("Style", "timeline-default-style",
873873
"tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles);
874874
@ <p>The default timeline viewing style, for when the user has not
875875
@ specified an alternative. (Property: "timeline-default-style")</p>
876876
877
- @ <hr />
877
+ @ <hr>
878878
entry_attribute("Default Number Of Rows", 6, "timeline-default-length",
879879
"tldl", "50", 0);
880880
@ <p>The maximum number of rows to show on a timeline in the absence
881881
@ of a user display preference cookie setting or an explicit n= query
882882
@ parameter. (Property: "timeline-default-length")</p>
883883
884
- @ <hr />
884
+ @ <hr>
885885
multiple_choice_attribute("Per-Item Time Format", "timeline-date-format",
886886
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
887887
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
888888
@ in a separate box (using CSS class "timelineDate") whenever the date
889889
@ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
890890
@ the complete date and time is shown on every timeline entry using the
891891
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
892892
893
- @ <hr />
893
+ @ <hr>
894894
entry_attribute("Max timeline comment length", 6,
895895
"timeline-max-comment", "tmc", "0", 0);
896896
@ <p>The maximum length of a comment to be displayed in a timeline.
897897
@ "0" there is no length limit.
898898
@ (Property: "timeline-max-comment")</p>
899899
900
- @ <hr />
900
+ @ <hr>
901901
entry_attribute("Tooltip dwell time (milliseconds)", 6,
902902
"timeline-dwelltime", "tdt", "100", 0);
903903
@ <br>
904904
entry_attribute("Tooltip close time (milliseconds)", 6,
905905
"timeline-closetime", "tct", "250", 0);
@@ -911,11 +911,11 @@
911911
@ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br>
912912
@ Set <strong>close time</strong> to "0" to keep tooltips visible until
913913
@ the mouse is clicked elsewhere.<p>
914914
@ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p>
915915
916
- @ <hr />
916
+ @ <hr>
917917
onoff_attribute("Timestamp hyperlinks to /info",
918918
"timeline-tslink-info", "ttlti", 0, 0);
919919
@ <p>The hyperlink on the timestamp associated with each timeline entry,
920920
@ on the far left-hand side of the screen, normally targets another
921921
@ /timeline page that shows the entry in context. However, if this
@@ -927,12 +927,12 @@
927927
@ double-clicking the timeline node or by clicking on the hash that
928928
@ follows "check-in:" in the supplemental information section on the
929929
@ right of the entry.
930930
@ <p>(Properties: "timeline-tslink-info")
931931
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>
934934
@ </div></form>
935935
db_end_transaction(0);
936936
style_finish_page();
937937
}
938938
@@ -964,11 +964,11 @@
964964
db_begin_transaction();
965965
@ <p>Settings marked with (v) are "versionable" and will be overridden
966966
@ by the contents of managed files named
967967
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
968968
@ 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>
970970
@
971971
@ <form action="%R/setup_settings" method="post"><div>
972972
@ <table border="0"><tr><td valign="top">
973973
login_insert_csrf_secret();
974974
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
@@ -978,17 +978,17 @@
978978
onoff_attribute("", pSet->name,
979979
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
980980
is_truth(pSet->def), hasVersionableValue);
981981
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
982982
if( pSet->versionable ){
983
- @ (v)<br />
983
+ @ (v)<br>
984984
} else {
985
- @ <br />
985
+ @ <br>
986986
}
987987
}
988988
}
989
- @ <br /><input type="submit" name="submit" value="Apply Changes" />
989
+ @ <br><input type="submit" name="submit" value="Apply Changes">
990990
@ </td><td style="width:50px;"></td><td valign="top">
991991
@ <table>
992992
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
993993
if( pSet->width>0 && !pSet->forceTextArea ){
994994
int hasVersionableValue = pSet->versionable &&
@@ -1012,18 +1012,18 @@
10121012
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
10131013
if( pSet->width>0 && pSet->forceTextArea ){
10141014
int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
10151015
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
10161016
if( pSet->versionable ){
1017
- @ (v)<br />
1017
+ @ (v)<br>
10181018
} else {
1019
- @ <br />
1019
+ @ <br>
10201020
}
10211021
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
10221022
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
10231023
(char*)pSet->def, hasVersionableValue);
1024
- @<br />
1024
+ @<br>
10251025
}
10261026
}
10271027
@ </td></tr></table>
10281028
@ </div></form>
10291029
db_end_transaction(0);
@@ -1097,24 +1097,24 @@
10971097
style_set_current_feature("setup");
10981098
style_header("WWW Configuration");
10991099
db_begin_transaction();
11001100
@ <form action="%R/setup_config" method="post"><div>
11011101
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>
11041104
entry_attribute("Project Name", 60, "project-name", "pn", "", 0);
11051105
@ <p>A brief project name so visitors know what this site is about.
11061106
@ The project name will also be used as the RSS feed title.
11071107
@ (Property: "project-name")
11081108
@ </p>
1109
- @ <hr />
1109
+ @ <hr>
11101110
textarea_attribute("Project Description", 3, 80,
11111111
"project-description", "pd", "", 0);
11121112
@ <p>Describe your project. This will be used in page headers for search
11131113
@ engines as well as a short RSS description.
11141114
@ (Property: "project-description")</p>
1115
- @ <hr />
1115
+ @ <hr>
11161116
entry_attribute("Canonical Server URL", 40, "email-url",
11171117
"eurl", "", 0);
11181118
@ <p>This is the URL used to access this repository as a server.
11191119
@ Other repositories use this URL to clone or sync against this repository.
11201120
@ This is also the basename for hyperlinks included in email alert text.
@@ -1130,20 +1130,20 @@
11301130
@ ZIP archive. For best results, keep this prefix brief and avoid special
11311131
@ characters such as "/" and "\".
11321132
@ If no tarball prefix is specified, then the full Project Name above is used.
11331133
@ (Property: "short-project-name")
11341134
@ </p>
1135
- @ <hr />
1135
+ @ <hr>
11361136
entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
11371137
@ <p>The <a href='%R/download'>/download</a> page is designed to provide
11381138
@ a convenient place for newbies
11391139
@ to download a ZIP archive or a tarball of the project. By default,
11401140
@ the latest trunk check-in is downloaded. Change this tag to something
11411141
@ else (ex: release) to alter the behavior of the /download page.
11421142
@ (Property: "download-tag")
11431143
@ </p>
1144
- @ <hr />
1144
+ @ <hr>
11451145
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
11461146
@ <p>Enter the pathname of the page to display when the "Home" menu
11471147
@ option is selected and when no pathname is
11481148
@ specified in the URL. For example, if you visit the url:</p>
11491149
@
@@ -1220,12 +1220,12 @@
12201220
@ <p>The default value is blank, meaning no added entries.
12211221
@ (Property: sitemap-extra)
12221222
@ <p>
12231223
textarea_attribute("Custom Sitemap Entries", 8, 80,
12241224
"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>
12271227
@ </div></form>
12281228
db_end_transaction(0);
12291229
style_finish_page();
12301230
}
12311231
@@ -1244,12 +1244,12 @@
12441244
style_set_current_feature("setup");
12451245
style_header("Wiki Configuration");
12461246
db_begin_transaction();
12471247
@ <form action="%R/setup_wiki" method="post"><div>
12481248
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>
12511251
onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
12521252
"wiki-about", "wiki-about", 1, 0);
12531253
@ <p>
12541254
@ Associate wiki pages with branches, tags, or checkins, based on
12551255
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/"
@@ -1259,11 +1259,11 @@
12591259
@ <li> <b>branch/</b><i>branch-name</i>
12601260
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
12611261
@ <li> <b>tag/</b><i>tag-name</i>
12621262
@ </ul>
12631263
@ (Property: "wiki-about")</p>
1264
- @ <hr />
1264
+ @ <hr>
12651265
entry_attribute("Allow Unsafe HTML In Markdown", 6,
12661266
"safe-html", "safe-html", "", 0);
12671267
@ <p>Allow "unsafe" HTML (ex: &lt;script&gt;, &lt;form&gt;, etc) to be
12681268
@ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
12691269
@ This setting is a string where each character indicates a "type" of
@@ -1279,16 +1279,16 @@
12791279
@ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except
12801280
@ in forum posts, make this setting be "<b>btw</b>". The default is an
12811281
@ empty string which means that Fossil never allows Markdown documents
12821282
@ to generate unsafe HTML.
12831283
@ (Property: "safe-html")</p>
1284
- @ <hr />
1284
+ @ <hr>
12851285
@ The current interwiki tag map is as follows:
12861286
interwiki_append_map_table(cgi_output_blob());
12871287
@ <p>Visit <a href="./intermap">%R/intermap</a> for details or to
12881288
@ modify the interwiki tag map.
1289
- @ <hr />
1289
+ @ <hr>
12901290
onoff_attribute("Use HTML as wiki markup language",
12911291
"wiki-use-html", "wiki-use-html", 0, 0);
12921292
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
12931293
@ but all other wiki formatting will be ignored.</p>
12941294
@ <p><strong>CAUTION:</strong> when
@@ -1297,12 +1297,12 @@
12971297
@ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
12981298
@ <p>This should <strong>only</strong> be enabled when wiki editing is limited
12991299
@ to trusted users. It should <strong>not</strong> be used on a publicly
13001300
@ editable wiki.</p>
13011301
@ (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>
13041304
@ </div></form>
13051305
db_end_transaction(0);
13061306
style_finish_page();
13071307
}
13081308
@@ -1328,32 +1328,32 @@
13281328
style_set_current_feature("setup");
13291329
style_header("Chat Configuration");
13301330
db_begin_transaction();
13311331
@ <form action="%R/setup_chat" method="post"><div>
13321332
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>
13351335
entry_attribute("Initial Chat History Size", 10,
13361336
"chat-initial-history", "chatih", "50", 0);
13371337
@ <p>When /chat first starts up, it preloads up to this many historical
13381338
@ messages.
13391339
@ (Property: "chat-initial-history")</p>
1340
- @ <hr />
1340
+ @ <hr>
13411341
entry_attribute("Minimum Number Of Historical Messages To Retain", 10,
13421342
"chat-keep-count", "chatkc", "50", 0);
13431343
@ <p>The chat subsystem purges older messages. But it will always retain
13441344
@ the N most recent messages where N is the value of this setting.
13451345
@ (Property: "chat-keep-count")</p>
1346
- @ <hr />
1346
+ @ <hr>
13471347
entry_attribute("Maximum Message Age In Days", 10,
13481348
"chat-keep-days", "chatkd", "7", 0);
13491349
@ <p>Chat message are removed after N days, where N is the value of
13501350
@ this setting. N may be fractional. So, for example, to only keep
13511351
@ an historical record of chat messages for 12 hours, set this value
13521352
@ to 0.5.
13531353
@ (Property: "chat-keep-days")</p>
1354
- @ <hr />
1354
+ @ <hr>
13551355
entry_attribute("Chat Polling Timeout", 10,
13561356
"chat-poll-timeout", "chatpt", "420", 0);
13571357
@ <p>New chat content is downloaded using the "long poll" technique.
13581358
@ HTTP requests are made to /chat-poll which blocks waiting on new
13591359
@ content to arrive. But the /chat-poll cannot block forever. It
@@ -1362,28 +1362,28 @@
13621362
@ default setting of approximately 7 minutes works well on many systems.
13631363
@ Shorter delays might be required on installations that use proxies
13641364
@ or web-servers with short timeouts. For best efficiency, this value
13651365
@ should be larger rather than smaller.
13661366
@ (Property: "chat-poll-timeout")</p>
1367
- @ <hr />
1367
+ @ <hr>
13681368
entry_attribute("Chat Timeline Robot Username", 15,
13691369
"chat-timeline-user", "chatrobot", "", 0);
13701370
@ <p>If this setting is not an empty string, then any changes that appear
13711371
@ on the timeline are announced in the chatroom under the username
13721372
@ supplied. The username does not need to actually exist in the USER table.
13731373
@ Suggested username: "chat-robot".
13741374
@ (Property: "chat-timeline-user")</p>
1375
- @ <hr />
1375
+ @ <hr>
13761376
13771377
multiple_choice_attribute("Alert sound",
13781378
"chat-alert-sound", "snd", azAlerts[0],
13791379
count(azAlerts)/2, azAlerts);
13801380
@ <p>The sound used in the client-side chat to indicate that a new
13811381
@ chat message has arrived.
13821382
@ (Property: "chat-alert-sound")</p>
13831383
@ <hr/>
1384
- @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
1384
+ @ <p><input type="submit" name="submit" value="Apply Changes"></p>
13851385
@ </div></form>
13861386
db_end_transaction(0);
13871387
@ <script nonce="%h(style_nonce())">
13881388
@ (function(){
13891389
@ var w = document.getElementById('idsnd');
@@ -1412,11 +1412,11 @@
14121412
style_set_current_feature("setup");
14131413
style_header("Moderator For Wiki And Tickets");
14141414
db_begin_transaction();
14151415
@ <form action="%R/setup_modreq" method="post"><div>
14161416
login_insert_csrf_secret();
1417
- @ <hr />
1417
+ @ <hr>
14181418
onoff_attribute("Moderate ticket changes",
14191419
"modreq-tkt", "modreq-tkt", 0, 0);
14201420
@ <p>When enabled, any change to tickets is subject to the approval
14211421
@ by a ticket moderator - a user with the "q" or Mod-Tkt privilege.
14221422
@ Ticket changes enter the system and are shown locally, but are not
@@ -1423,11 +1423,11 @@
14231423
@ synced until they are approved. The moderator has the option to
14241424
@ delete the change rather than approve it. Ticket changes made by
14251425
@ a user who has the Mod-Tkt privilege are never subject to
14261426
@ moderation. (Property: "modreq-tkt")
14271427
@
1428
- @ <hr />
1428
+ @ <hr>
14291429
onoff_attribute("Moderate wiki changes",
14301430
"modreq-wiki", "modreq-wiki", 0, 0);
14311431
@ <p>When enabled, any change to wiki is subject to the approval
14321432
@ by a wiki moderator - a user with the "l" or Mod-Wiki privilege.
14331433
@ Wiki changes enter the system and are shown locally, but are not
@@ -1435,12 +1435,12 @@
14351435
@ delete the change rather than approve it. Wiki changes made by
14361436
@ a user who has the Mod-Wiki privilege are never subject to
14371437
@ moderation. (Property: "modreq-wiki")
14381438
@ </p>
14391439
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>
14421442
@ </div></form>
14431443
db_end_transaction(0);
14441444
style_finish_page();
14451445
14461446
}
@@ -1469,29 +1469,29 @@
14691469
14701470
style_set_current_feature("setup");
14711471
style_header("Edit Ad Unit");
14721472
@ <form action="%R/setup_adunit" method="post"><div>
14731473
login_insert_csrf_secret();
1474
- @ <b>Banner Ad-Unit:</b><br />
1474
+ @ <b>Banner Ad-Unit:</b><br>
14751475
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>
14781478
textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0);
1479
- @ <br />
1479
+ @ <br>
14801480
onoff_attribute("Omit ads to administrator",
14811481
"adunit-omit-if-admin", "oia", 0, 0);
1482
- @ <br />
1482
+ @ <br>
14831483
onoff_attribute("Omit ads to logged-in users",
14841484
"adunit-omit-if-user", "oiu", 0, 0);
1485
- @ <br />
1485
+ @ <br>
14861486
onoff_attribute("Temporarily disable all ads",
14871487
"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">
14911491
@ </div></form>
1492
- @ <hr />
1492
+ @ <hr>
14931493
@ <b>Ad-Unit Notes:</b><ul>
14941494
@ <li>Leave both Ad-Units blank to disable all advertising.
14951495
@ <li>The "Banner Ad-Unit" is used for wide pages.
14961496
@ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content.
14971497
@ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is
@@ -1651,12 +1651,12 @@
16511651
}
16521652
style_set_current_feature("setup");
16531653
style_header("Edit Project Logo And Background");
16541654
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
16551655
@ 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">
16581658
@ </p></blockquote>
16591659
@
16601660
@ <form action="%R/setup_logo" method="post"
16611661
@ enctype="multipart/form-data"><div>
16621662
@ <p>The logo is accessible to all users at this URL:
@@ -1665,22 +1665,22 @@
16651665
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
16661666
@ <a href="setup_skinedit?w=2">header setup</a>.
16671667
@ To change the logo image, use the following form:</p>
16681668
login_insert_csrf_secret();
16691669
@ Logo Image file:
1670
- @ <input type="file" name="logoim" size="60" accept="image/*" />
1670
+ @ <input type="file" name="logoim" size="60" accept="image/*">
16711671
@ <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>
16741674
@ <p>(Properties: "logo-image" and "logo-mimetype")
16751675
@ </div></form>
1676
- @ <hr />
1676
+ @ <hr>
16771677
@
16781678
@ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
16791679
@ and looks like this:</p>
16801680
@ <blockquote><p><img src="%R/background/%z(zBgMtime)" \
1681
- @ alt="background" border=1 />
1681
+ @ alt="background" border=1>
16821682
@ </p></blockquote>
16831683
@
16841684
@ <form action="%R/setup_logo" method="post"
16851685
@ enctype="multipart/form-data"><div>
16861686
@ <p>The background image is accessible to all users at this URL:
@@ -1689,22 +1689,22 @@
16891689
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
16901690
@ <a href="setup_skinedit?w=2">header setup</a>.
16911691
@ To change the background image, use the following form:</p>
16921692
login_insert_csrf_secret();
16931693
@ Background image file:
1694
- @ <input type="file" name="bgim" size="60" accept="image/*" />
1694
+ @ <input type="file" name="bgim" size="60" accept="image/*">
16951695
@ <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>
16981698
@ </div></form>
16991699
@ <p>(Properties: "background-image" and "background-mimetype")
1700
- @ <hr />
1700
+ @ <hr>
17011701
@
17021702
@ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b>
17031703
@ and looks like this:</p>
17041704
@ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \
1705
- @ alt="icon" border=1 />
1705
+ @ alt="icon" border=1>
17061706
@ </p></blockquote>
17071707
@
17081708
@ <form action="%R/setup_logo" method="post"
17091709
@ enctype="multipart/form-data"><div>
17101710
@ <p>The icon image is accessible to all users at this URL:
@@ -1713,17 +1713,17 @@
17131713
@ page depending on the web browser in use and the MIME-Types that it
17141714
@ supports for icon images.
17151715
@ To change the icon image, use the following form:</p>
17161716
login_insert_csrf_secret();
17171717
@ Icon image file:
1718
- @ <input type="file" name="iconim" size="60" accept="image/*" />
1718
+ @ <input type="file" name="iconim" size="60" accept="image/*">
17191719
@ <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>
17221722
@ </div></form>
17231723
@ <p>(Properties: "icon-image" and "icon-mimetype")
1724
- @ <hr />
1724
+ @ <hr>
17251725
@
17261726
@ <p><span class="note">Note:</span> Your browser has probably cached these
17271727
@ images, so you may need to press the Reload button before changes will
17281728
@ take effect. </p>
17291729
style_finish_page();
@@ -1804,12 +1804,12 @@
18041804
go = 1;
18051805
}
18061806
@
18071807
@ <form method="post" action="%R/admin_sql">
18081808
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>
18111811
@ <input type="submit" name="go" value="Run SQL">
18121812
@ <input type="submit" name="schema" value="Show Schema">
18131813
@ <input type="submit" name="tablelist" value="List Tables">
18141814
@ <input type="submit" name="configtab" value="CONFIG Table Query">
18151815
@ </form>
@@ -1827,11 +1827,11 @@
18271827
int rc;
18281828
const char *zTail;
18291829
int nCol;
18301830
int nRow = 0;
18311831
int i;
1832
- @ <hr />
1832
+ @ <hr>
18331833
login_verify_csrf_secret();
18341834
sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
18351835
search_sql_setup(g.db);
18361836
rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
18371837
if( rc!=SQLITE_OK ){
@@ -1913,19 +1913,19 @@
19131913
@ run by this page. If Tcl integration was enabled at compile-time and
19141914
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
19151915
@
19161916
@ <form method="post" action="%R/admin_th1">
19171917
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>
19201920
@ <input type="submit" name="go" value="Run TH1">
19211921
@ </form>
19221922
if( go ){
19231923
const char *zR;
19241924
int rc;
19251925
int n;
1926
- @ <hr />
1926
+ @ <hr>
19271927
login_verify_csrf_secret();
19281928
rc = Th_Eval(g.interp, 0, zQ, -1);
19291929
zR = Th_GetResult(g.interp, &n);
19301930
if( rc==TH_OK ){
19311931
@ <pre class="th1result">%h(zR)</pre>
@@ -2005,10 +2005,25 @@
20052005
@ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
20062006
}
20072007
style_finish_page();
20082008
}
20092009
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
+
20102025
/*
20112026
** WEBPAGE: srchsetup
20122027
**
20132028
** Configure the search engine. Requires Admin privilege.
20142029
*/
@@ -2024,11 +2039,11 @@
20242039
login_insert_csrf_secret();
20252040
@ <div style="text-align:center;font-weight:bold;">
20262041
@ Server-specific settings that affect the
20272042
@ <a href="%R/search">/search</a> webpage.
20282043
@ </div>
2029
- @ <hr />
2044
+ @ <hr>
20302045
textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
20312046
@ <p>The "Document Glob List" is a comma- or newline-separated list
20322047
@ of GLOB expressions that identify all documents within the source
20332048
@ tree that are to be searched when "Document Search" is enabled.
20342049
@ Some examples:
@@ -2040,51 +2055,53 @@
20402055
@ files.</tr>
20412056
@ <tr><td>*<td><td>Search all checked-in files</tr>
20422057
@ <tr><td><i>(blank)</i><td>
20432058
@ <td>Search nothing. (Disables document search).</tr>
20442059
@ </table>
2045
- @ <hr />
2060
+ @ <hr>
20462061
entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
20472062
@ <p>When searching documents, use the versions of the files found at the
20482063
@ type of the "Document Branch" branch. Recommended value: "trunk".
20492064
@ Document search is disabled if blank.
2050
- @ <hr />
2065
+ @ <hr>
20512066
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
2052
- @ <br />
2067
+ @ <br>
20532068
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
2054
- @ <br />
2069
+ @ <br>
20552070
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
2056
- @ <br />
2071
+ @ <br>
20572072
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
2058
- @ <br />
2073
+ @ <br>
20592074
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
2060
- @ <br />
2075
+ @ <br>
20612076
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>
20652080
if( P("fts0") ){
20662081
search_drop_index();
20672082
}else if( P("fts1") ){
2083
+ const char *zTokenizer = PD("ftstok","off");
2084
+ search_set_tokenizer(zTokenizer);
20682085
search_drop_index();
20692086
search_create_index();
20702087
search_fill_index();
20712088
search_update_index(search_restrict(SRCH_ALL));
20722089
}
20732090
if( search_index_exists() ){
20742091
@ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index.
20752092
@ The index helps search run faster, especially on large repositories,
20762093
@ but takes up space.</p>
2077
- onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
2094
+ select_fts_tokenizer();
20782095
@ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
20792096
@ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
20802097
style_submenu_element("FTS Index Debugging","%R/test-ftsdocs");
20812098
}else{
20822099
@ <p>The SQLite search index is disabled. All searching will be
20832100
@ a full-text scan. This usually works fine, but can be slow for
20842101
@ larger repositories.</p>
2085
- onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
2102
+ select_fts_tokenizer();
20862103
@ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
20872104
}
20882105
@ </div></form>
20892106
style_finish_page();
20902107
}
20912108
--- 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&nbsp;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: &lt;script&gt;, &lt;form&gt;, 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&nbsp;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: &lt;script&gt;, &lt;form&gt;, 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 @@
219219
@ checked="checked" \
220220
}
221221
if( disabled ){
222222
@ disabled="disabled" \
223223
}
224
- @ /> <b>%s(zLabel)</b></label>
224
+ @ > <b>%s(zLabel)</b></label>
225225
}
226226
227227
/*
228228
** Generate an entry box for an attribute.
229229
*/
@@ -251,11 +251,11 @@
251251
@ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \
252252
@ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
253253
if( disabled ){
254254
@ disabled="disabled" \
255255
}
256
- @ /> <b>%s(zLabel)</b>
256
+ @ > <b>%s(zLabel)</b>
257257
}
258258
259259
/*
260260
** Generate a text box for an attribute.
261261
*/
@@ -371,11 +371,11 @@
371371
@
372372
@ <p>Additional parameters that control the behavior of Javascript:</p>
373373
@ <blockquote>
374374
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
375375
"auto-hyperlink-delay", "ah-delay", "50", 0);
376
- @ <br />
376
+ @ <br>
377377
onoff_attribute("Also require a mouse event before enabling hyperlinks",
378378
"auto-hyperlink-mouseover", "ahmo", 0, 0);
379379
@ </blockquote>
380380
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
381381
@ and "require a mouse event" should be turned on. These values only come
@@ -412,15 +412,15 @@
412412
@ <p>The settings on this page are intended to help site administrators
413413
@ defend the site against robots.
414414
@
415415
@ <form action="%R/setup_robot" method="post"><div>
416416
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>
419419
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>
422422
@ </div></form>
423423
db_end_transaction(0);
424424
style_finish_page();
425425
}
426426
@@ -444,12 +444,12 @@
444444
style_set_current_feature("setup");
445445
style_header("Access Control Settings");
446446
db_begin_transaction();
447447
@ <form action="%R/setup_access" method="post"><div>
448448
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>
451451
multiple_choice_attribute("Redirect to HTTPS",
452452
"redirect-to-https", "redirhttps", "0",
453453
count(azRedirectOpts)/2, azRedirectOpts);
454454
@ <p>Force the use of HTTPS by redirecting to HTTPS when an
455455
@ unencrypted request is received. This feature can be enabled
@@ -457,11 +457,11 @@
457457
@ <p>Further details: When enabled, this option causes the $secureurl TH1
458458
@ variable is set to an "https:" variant of $baseurl. Otherwise,
459459
@ $secureurl is just an alias for $baseurl.
460460
@ (Property: "redirect-to-https". "0" for off, "1" for Login page only,
461461
@ "2" otherwise.)
462
- @ <hr />
462
+ @ <hr>
463463
onoff_attribute("Require password for local access",
464464
"localauth", "localauth", 0, 0);
465465
@ <p>When enabled, the password sign-in is always required for
466466
@ web access. When disabled, unrestricted web access from 127.0.0.1
467467
@ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
@@ -483,64 +483,64 @@
483483
@ <li> The server is started from CGI without the "localauth" keyword
484484
@ in the CGI script.
485485
@ </ol>
486486
@ (Property: "localauth")
487487
@
488
- @ <hr />
488
+ @ <hr>
489489
onoff_attribute("Enable /test_env",
490490
"test_env_enable", "test_env_enable", 0, 0);
491491
@ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
492492
@ users. When disabled (the default) only users Admin and Setup can visit
493493
@ the /test_env page.
494494
@ (Property: "test_env_enable")
495495
@ </p>
496496
@
497
- @ <hr />
497
+ @ <hr>
498498
onoff_attribute("Enable /artifact_stats",
499499
"artifact_stats_enable", "artifact_stats_enable", 0, 0);
500500
@ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all
501501
@ users. When disabled (the default) only users with check-in privilege may
502502
@ access the /artifact_stats page.
503503
@ (Property: "artifact_stats_enable")
504504
@ </p>
505505
@
506
- @ <hr />
506
+ @ <hr>
507507
onoff_attribute("Allow REMOTE_USER authentication",
508508
"remote_user_ok", "remote_user_ok", 0, 0);
509509
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
510510
@ login name of a valid user and no other login credentials are available,
511511
@ then the REMOTE_USER is accepted as an authenticated user.
512512
@ (Property: "remote_user_ok")
513513
@ </p>
514514
@
515
- @ <hr />
515
+ @ <hr>
516516
onoff_attribute("Allow HTTP_AUTHENTICATION authentication",
517517
"http_authentication_ok", "http_authentication_ok", 0, 0);
518518
@ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment
519519
@ variable or the "Authentication:" HTTP header to find the username and
520520
@ password. This is another way of supporting Basic Authentication.
521521
@ (Property: "http_authentication_ok")
522522
@ </p>
523523
@
524
- @ <hr />
524
+ @ <hr>
525525
entry_attribute("Login expiration time", 6, "cookie-expire", "cex",
526526
"8766", 0);
527527
@ <p>The number of hours for which a login is valid. This must be a
528528
@ positive number. The default is 8766 hours which is approximately equal
529529
@ to a year.
530530
@ (Property: "cookie-expire")</p>
531531
532
- @ <hr />
532
+ @ <hr>
533533
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
534534
"5000000", 0);
535535
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
536536
@ to this many bytes, uncompressed. If the client requires more data
537537
@ than this, then the client will issue multiple HTTP requests.
538538
@ Values below 1 million are not recommended. 5 million is a
539539
@ reasonable number. (Property: "max-download")</p>
540540
541
- @ <hr />
541
+ @ <hr>
542542
entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
543543
"30", 0);
544544
545545
@ <p>Fossil tries to spend less than this many seconds gathering
546546
@ the out-bound data of sync, clone, and pull packets.
@@ -547,11 +547,11 @@
547547
@ If the client request takes longer, a partial reply is given similar
548548
@ to the download packet limit. 30s is a reasonable default.
549549
@ (Property: "max-download-time")</p>
550550
551551
@ <a id="slal"></a>
552
- @ <hr />
552
+ @ <hr>
553553
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
554554
"0.0", 0);
555555
@ <p>Some expensive operations (such as computing tarballs, zip archives,
556556
@ or annotation/blame pages) are prohibited if the load average on the host
557557
@ computer is too large. Set the threshold for disallowing expensive
@@ -562,21 +562,21 @@
562562
@ (Property: "max-loadavg")</p>
563563
564564
/* Add the auto-hyperlink settings controls. These same controls
565565
** are also accessible from the /setup_robot page.
566566
*/
567
- @ <hr />
567
+ @ <hr>
568568
addAutoHyperlinkSettings();
569569
570
- @ <hr />
570
+ @ <hr>
571571
onoff_attribute("Require a CAPTCHA if not logged in",
572572
"require-captcha", "reqcapt", 1, 0);
573573
@ <p>Require a CAPTCHA for edit operations (appending, creating, or
574574
@ editing wiki or tickets or adding attachments to wiki or tickets)
575575
@ for users who are not logged in. (Property: "require-captcha")</p>
576576
577
- @ <hr />
577
+ @ <hr>
578578
entry_attribute("Public pages", 30, "public-pages",
579579
"pubpage", "", 0);
580580
@ <p>A comma-separated list of glob patterns for pages that are accessible
581581
@ without needing a login and using the privileges given by the
582582
@ "Default privileges" setting below.
@@ -587,57 +587,57 @@
587587
@ latest version of the embedded documentation in the www/ folder without
588588
@ allowing them to see the rest of the source code.
589589
@ (Property: "public-pages")
590590
@ </p>
591591
592
- @ <hr />
592
+ @ <hr>
593593
onoff_attribute("Allow users to register themselves",
594594
"self-register", "selfreg", 0, 0);
595595
@ <p>Allow users to register themselves on the /register webpage.
596596
@ A self-registration creates a new entry in the USER table and
597597
@ perhaps also in the SUBSCRIBER table if email notification is
598598
@ enabled.
599599
@ (Property: "self-register")</p>
600600
601
- @ <hr />
601
+ @ <hr>
602602
onoff_attribute("Allow users to reset their own passwords",
603603
"self-pw-reset", "selfpw", 0, 0);
604604
@ <p>Allow users to request that an email contains a hyperlink to a
605605
@ password reset page be sent to their email address of record. This
606606
@ enables forgetful users to recover their forgotten passwords without
607607
@ administrator intervention.
608608
@ (Property: "self-pw-reset")</p>
609609
610
- @ <hr />
610
+ @ <hr>
611611
onoff_attribute("Email verification required for self-registration",
612612
"selfreg-verify", "sfverify", 0, 0);
613613
@ <p>If enabled, self-registration creates a new entry in the USER table
614614
@ with only capabilities "7". The default user capabilities are not
615615
@ added until the email address associated with the self-registration
616616
@ has been verified. This setting only makes sense if
617617
@ email notifications are enabled.
618618
@ (Property: "selfreg-verify")</p>
619619
620
- @ <hr />
620
+ @ <hr>
621621
onoff_attribute("Allow anonymous subscriptions",
622622
"anon-subscribe", "anonsub", 1, 0);
623623
@ <p>If disabled, email notification subscriptions are only allowed
624624
@ for users with a login. If Nobody or Anonymous visit the /subscribe
625625
@ page, they are redirected to /register or /login.
626626
@ (Property: "anon-subscribe")</p>
627627
628
- @ <hr />
628
+ @ <hr>
629629
entry_attribute("Authorized subscription email addresses", 35,
630630
"auth-sub-email", "asemail", "", 0);
631631
@ <p>This is a comma-separated list of GLOB patterns that specify
632632
@ email addresses that are authorized to subscriptions. If blank
633633
@ (the usual case), then any email address can be used to self-register.
634634
@ This setting is used to limit subscriptions to members of a particular
635635
@ organization or group based on their email address.
636636
@ (Property: "auth-sub-email")</p>
637637
638
- @ <hr />
638
+ @ <hr>
639639
entry_attribute("Default privileges", 10, "default-perms",
640640
"defaultperms", "u", 0);
641641
@ <p>Permissions given to users that... <ul><li>register themselves using
642642
@ the self-registration procedure (if enabled), or <li>access "public"
643643
@ pages identified by the public-pages glob pattern above, or <li>
@@ -645,21 +645,21 @@
645645
@ <p>Recommended value: "u" for Reader.
646646
@ <a href="%R/setup_ucap_list">Capability Key</a>.
647647
@ (Property: "default-perms")
648648
@ </p>
649649
650
- @ <hr />
650
+ @ <hr>
651651
onoff_attribute("Show javascript button to fill in CAPTCHA",
652652
"auto-captcha", "autocaptcha", 0, 0);
653653
@ <p>When enabled, a button appears on the login screen for user
654654
@ "anonymous" that will automatically fill in the CAPTCHA password.
655655
@ This is less secure than forcing the user to do it manually, but is
656656
@ probably secure enough and it is certainly more convenient for
657657
@ anonymous users. (Property: "auto-captcha")</p>
658658
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>
661661
@ </div></form>
662662
db_end_transaction(0);
663663
style_finish_page();
664664
}
665665
@@ -762,11 +762,11 @@
762762
@ <p><form action="%R/setup_login_group" method="post"><div>
763763
login_insert_csrf_secret();
764764
@ To leave this login group press
765765
@ <input type="submit" value="Leave Login Group" name="leave">
766766
@ </form></p>
767
- @ <hr /><h2>Implementation Details</h2>
767
+ @ <hr><h2>Implementation Details</h2>
768768
@ <p>The following are fields from the CONFIG table related to login-groups,
769769
@ provided here for instructional and debugging purposes:</p>
770770
@ <table border='1' class='sortable' data-column-types='ttt' \
771771
@ data-init-sort='1'>
772772
@ <thead><tr>
@@ -814,42 +814,42 @@
814814
style_set_current_feature("setup");
815815
style_header("Timeline Display Preferences");
816816
db_begin_transaction();
817817
@ <form action="%R/setup_timeline" method="post"><div>
818818
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>
820820
821
- @ <hr />
821
+ @ <hr>
822822
onoff_attribute("Allow block-markup in timeline",
823823
"timeline-block-markup", "tbm", 0, 0);
824824
@ <p>In timeline displays, check-in comments can be displayed with or
825825
@ without block markup such as paragraphs, tables, etc.
826826
@ (Property: "timeline-block-markup")</p>
827827
828
- @ <hr />
828
+ @ <hr>
829829
onoff_attribute("Plaintext comments on timelines",
830830
"timeline-plaintext", "tpt", 0, 0);
831831
@ <p>In timeline displays, check-in comments are displayed literally,
832832
@ without any wiki or HTML interpretation. Use CSS to change
833833
@ display formatting features such as fonts and line-wrapping behavior.
834834
@ (Property: "timeline-plaintext")</p>
835835
836
- @ <hr />
836
+ @ <hr>
837837
onoff_attribute("Truncate comment at first blank line (Git-style)",
838838
"timeline-truncate-at-blank", "ttb", 0, 0);
839839
@ <p>In timeline displays, check-in comments are displayed only through
840840
@ the first blank line. This is the traditional way to display comments
841841
@ in Git repositories (Property: "timeline-truncate-at-blank")</p>
842842
843
- @ <hr />
843
+ @ <hr>
844844
onoff_attribute("Break comments at newline characters",
845845
"timeline-hard-newlines", "thnl", 0, 0);
846846
@ <p>In timeline displays, newline characters in check-in comments force
847847
@ a line break on the display.
848848
@ (Property: "timeline-hard-newlines")</p>
849849
850
- @ <hr />
850
+ @ <hr>
851851
onoff_attribute("Use Universal Coordinated Time (UTC)",
852852
"timeline-utc", "utc", 1, 0);
853853
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
854854
@ Zulu) instead of in local time. On this server, local time is currently
855855
tmDiff = db_double(0.0, "SELECT julianday('now')");
@@ -866,40 +866,40 @@
866866
}else{
867867
@ %s(zTmDiff) hours ahead of UTC.</p>
868868
}
869869
@ <p>(Property: "timeline-utc")
870870
871
- @ <hr />
871
+ @ <hr>
872872
multiple_choice_attribute("Style", "timeline-default-style",
873873
"tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles);
874874
@ <p>The default timeline viewing style, for when the user has not
875875
@ specified an alternative. (Property: "timeline-default-style")</p>
876876
877
- @ <hr />
877
+ @ <hr>
878878
entry_attribute("Default Number Of Rows", 6, "timeline-default-length",
879879
"tldl", "50", 0);
880880
@ <p>The maximum number of rows to show on a timeline in the absence
881881
@ of a user display preference cookie setting or an explicit n= query
882882
@ parameter. (Property: "timeline-default-length")</p>
883883
884
- @ <hr />
884
+ @ <hr>
885885
multiple_choice_attribute("Per-Item Time Format", "timeline-date-format",
886886
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
887887
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
888888
@ in a separate box (using CSS class "timelineDate") whenever the date
889889
@ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
890890
@ the complete date and time is shown on every timeline entry using the
891891
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
892892
893
- @ <hr />
893
+ @ <hr>
894894
entry_attribute("Max timeline comment length", 6,
895895
"timeline-max-comment", "tmc", "0", 0);
896896
@ <p>The maximum length of a comment to be displayed in a timeline.
897897
@ "0" there is no length limit.
898898
@ (Property: "timeline-max-comment")</p>
899899
900
- @ <hr />
900
+ @ <hr>
901901
entry_attribute("Tooltip dwell time (milliseconds)", 6,
902902
"timeline-dwelltime", "tdt", "100", 0);
903903
@ <br>
904904
entry_attribute("Tooltip close time (milliseconds)", 6,
905905
"timeline-closetime", "tct", "250", 0);
@@ -911,11 +911,11 @@
911911
@ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br>
912912
@ Set <strong>close time</strong> to "0" to keep tooltips visible until
913913
@ the mouse is clicked elsewhere.<p>
914914
@ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p>
915915
916
- @ <hr />
916
+ @ <hr>
917917
onoff_attribute("Timestamp hyperlinks to /info",
918918
"timeline-tslink-info", "ttlti", 0, 0);
919919
@ <p>The hyperlink on the timestamp associated with each timeline entry,
920920
@ on the far left-hand side of the screen, normally targets another
921921
@ /timeline page that shows the entry in context. However, if this
@@ -927,12 +927,12 @@
927927
@ double-clicking the timeline node or by clicking on the hash that
928928
@ follows "check-in:" in the supplemental information section on the
929929
@ right of the entry.
930930
@ <p>(Properties: "timeline-tslink-info")
931931
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>
934934
@ </div></form>
935935
db_end_transaction(0);
936936
style_finish_page();
937937
}
938938
@@ -964,11 +964,11 @@
964964
db_begin_transaction();
965965
@ <p>Settings marked with (v) are "versionable" and will be overridden
966966
@ by the contents of managed files named
967967
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
968968
@ 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>
970970
@
971971
@ <form action="%R/setup_settings" method="post"><div>
972972
@ <table border="0"><tr><td valign="top">
973973
login_insert_csrf_secret();
974974
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
@@ -978,17 +978,17 @@
978978
onoff_attribute("", pSet->name,
979979
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
980980
is_truth(pSet->def), hasVersionableValue);
981981
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
982982
if( pSet->versionable ){
983
- @ (v)<br />
983
+ @ (v)<br>
984984
} else {
985
- @ <br />
985
+ @ <br>
986986
}
987987
}
988988
}
989
- @ <br /><input type="submit" name="submit" value="Apply Changes" />
989
+ @ <br><input type="submit" name="submit" value="Apply Changes">
990990
@ </td><td style="width:50px;"></td><td valign="top">
991991
@ <table>
992992
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
993993
if( pSet->width>0 && !pSet->forceTextArea ){
994994
int hasVersionableValue = pSet->versionable &&
@@ -1012,18 +1012,18 @@
10121012
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
10131013
if( pSet->width>0 && pSet->forceTextArea ){
10141014
int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
10151015
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
10161016
if( pSet->versionable ){
1017
- @ (v)<br />
1017
+ @ (v)<br>
10181018
} else {
1019
- @ <br />
1019
+ @ <br>
10201020
}
10211021
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
10221022
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
10231023
(char*)pSet->def, hasVersionableValue);
1024
- @<br />
1024
+ @<br>
10251025
}
10261026
}
10271027
@ </td></tr></table>
10281028
@ </div></form>
10291029
db_end_transaction(0);
@@ -1097,24 +1097,24 @@
10971097
style_set_current_feature("setup");
10981098
style_header("WWW Configuration");
10991099
db_begin_transaction();
11001100
@ <form action="%R/setup_config" method="post"><div>
11011101
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>
11041104
entry_attribute("Project Name", 60, "project-name", "pn", "", 0);
11051105
@ <p>A brief project name so visitors know what this site is about.
11061106
@ The project name will also be used as the RSS feed title.
11071107
@ (Property: "project-name")
11081108
@ </p>
1109
- @ <hr />
1109
+ @ <hr>
11101110
textarea_attribute("Project Description", 3, 80,
11111111
"project-description", "pd", "", 0);
11121112
@ <p>Describe your project. This will be used in page headers for search
11131113
@ engines as well as a short RSS description.
11141114
@ (Property: "project-description")</p>
1115
- @ <hr />
1115
+ @ <hr>
11161116
entry_attribute("Canonical Server URL", 40, "email-url",
11171117
"eurl", "", 0);
11181118
@ <p>This is the URL used to access this repository as a server.
11191119
@ Other repositories use this URL to clone or sync against this repository.
11201120
@ This is also the basename for hyperlinks included in email alert text.
@@ -1130,20 +1130,20 @@
11301130
@ ZIP archive. For best results, keep this prefix brief and avoid special
11311131
@ characters such as "/" and "\".
11321132
@ If no tarball prefix is specified, then the full Project Name above is used.
11331133
@ (Property: "short-project-name")
11341134
@ </p>
1135
- @ <hr />
1135
+ @ <hr>
11361136
entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
11371137
@ <p>The <a href='%R/download'>/download</a> page is designed to provide
11381138
@ a convenient place for newbies
11391139
@ to download a ZIP archive or a tarball of the project. By default,
11401140
@ the latest trunk check-in is downloaded. Change this tag to something
11411141
@ else (ex: release) to alter the behavior of the /download page.
11421142
@ (Property: "download-tag")
11431143
@ </p>
1144
- @ <hr />
1144
+ @ <hr>
11451145
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
11461146
@ <p>Enter the pathname of the page to display when the "Home" menu
11471147
@ option is selected and when no pathname is
11481148
@ specified in the URL. For example, if you visit the url:</p>
11491149
@
@@ -1220,12 +1220,12 @@
12201220
@ <p>The default value is blank, meaning no added entries.
12211221
@ (Property: sitemap-extra)
12221222
@ <p>
12231223
textarea_attribute("Custom Sitemap Entries", 8, 80,
12241224
"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>
12271227
@ </div></form>
12281228
db_end_transaction(0);
12291229
style_finish_page();
12301230
}
12311231
@@ -1244,12 +1244,12 @@
12441244
style_set_current_feature("setup");
12451245
style_header("Wiki Configuration");
12461246
db_begin_transaction();
12471247
@ <form action="%R/setup_wiki" method="post"><div>
12481248
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>
12511251
onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
12521252
"wiki-about", "wiki-about", 1, 0);
12531253
@ <p>
12541254
@ Associate wiki pages with branches, tags, or checkins, based on
12551255
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/"
@@ -1259,11 +1259,11 @@
12591259
@ <li> <b>branch/</b><i>branch-name</i>
12601260
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
12611261
@ <li> <b>tag/</b><i>tag-name</i>
12621262
@ </ul>
12631263
@ (Property: "wiki-about")</p>
1264
- @ <hr />
1264
+ @ <hr>
12651265
entry_attribute("Allow Unsafe HTML In Markdown", 6,
12661266
"safe-html", "safe-html", "", 0);
12671267
@ <p>Allow "unsafe" HTML (ex: &lt;script&gt;, &lt;form&gt;, etc) to be
12681268
@ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
12691269
@ This setting is a string where each character indicates a "type" of
@@ -1279,16 +1279,16 @@
12791279
@ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except
12801280
@ in forum posts, make this setting be "<b>btw</b>". The default is an
12811281
@ empty string which means that Fossil never allows Markdown documents
12821282
@ to generate unsafe HTML.
12831283
@ (Property: "safe-html")</p>
1284
- @ <hr />
1284
+ @ <hr>
12851285
@ The current interwiki tag map is as follows:
12861286
interwiki_append_map_table(cgi_output_blob());
12871287
@ <p>Visit <a href="./intermap">%R/intermap</a> for details or to
12881288
@ modify the interwiki tag map.
1289
- @ <hr />
1289
+ @ <hr>
12901290
onoff_attribute("Use HTML as wiki markup language",
12911291
"wiki-use-html", "wiki-use-html", 0, 0);
12921292
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
12931293
@ but all other wiki formatting will be ignored.</p>
12941294
@ <p><strong>CAUTION:</strong> when
@@ -1297,12 +1297,12 @@
12971297
@ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
12981298
@ <p>This should <strong>only</strong> be enabled when wiki editing is limited
12991299
@ to trusted users. It should <strong>not</strong> be used on a publicly
13001300
@ editable wiki.</p>
13011301
@ (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>
13041304
@ </div></form>
13051305
db_end_transaction(0);
13061306
style_finish_page();
13071307
}
13081308
@@ -1328,32 +1328,32 @@
13281328
style_set_current_feature("setup");
13291329
style_header("Chat Configuration");
13301330
db_begin_transaction();
13311331
@ <form action="%R/setup_chat" method="post"><div>
13321332
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>
13351335
entry_attribute("Initial Chat History Size", 10,
13361336
"chat-initial-history", "chatih", "50", 0);
13371337
@ <p>When /chat first starts up, it preloads up to this many historical
13381338
@ messages.
13391339
@ (Property: "chat-initial-history")</p>
1340
- @ <hr />
1340
+ @ <hr>
13411341
entry_attribute("Minimum Number Of Historical Messages To Retain", 10,
13421342
"chat-keep-count", "chatkc", "50", 0);
13431343
@ <p>The chat subsystem purges older messages. But it will always retain
13441344
@ the N most recent messages where N is the value of this setting.
13451345
@ (Property: "chat-keep-count")</p>
1346
- @ <hr />
1346
+ @ <hr>
13471347
entry_attribute("Maximum Message Age In Days", 10,
13481348
"chat-keep-days", "chatkd", "7", 0);
13491349
@ <p>Chat message are removed after N days, where N is the value of
13501350
@ this setting. N may be fractional. So, for example, to only keep
13511351
@ an historical record of chat messages for 12 hours, set this value
13521352
@ to 0.5.
13531353
@ (Property: "chat-keep-days")</p>
1354
- @ <hr />
1354
+ @ <hr>
13551355
entry_attribute("Chat Polling Timeout", 10,
13561356
"chat-poll-timeout", "chatpt", "420", 0);
13571357
@ <p>New chat content is downloaded using the "long poll" technique.
13581358
@ HTTP requests are made to /chat-poll which blocks waiting on new
13591359
@ content to arrive. But the /chat-poll cannot block forever. It
@@ -1362,28 +1362,28 @@
13621362
@ default setting of approximately 7 minutes works well on many systems.
13631363
@ Shorter delays might be required on installations that use proxies
13641364
@ or web-servers with short timeouts. For best efficiency, this value
13651365
@ should be larger rather than smaller.
13661366
@ (Property: "chat-poll-timeout")</p>
1367
- @ <hr />
1367
+ @ <hr>
13681368
entry_attribute("Chat Timeline Robot Username", 15,
13691369
"chat-timeline-user", "chatrobot", "", 0);
13701370
@ <p>If this setting is not an empty string, then any changes that appear
13711371
@ on the timeline are announced in the chatroom under the username
13721372
@ supplied. The username does not need to actually exist in the USER table.
13731373
@ Suggested username: "chat-robot".
13741374
@ (Property: "chat-timeline-user")</p>
1375
- @ <hr />
1375
+ @ <hr>
13761376
13771377
multiple_choice_attribute("Alert sound",
13781378
"chat-alert-sound", "snd", azAlerts[0],
13791379
count(azAlerts)/2, azAlerts);
13801380
@ <p>The sound used in the client-side chat to indicate that a new
13811381
@ chat message has arrived.
13821382
@ (Property: "chat-alert-sound")</p>
13831383
@ <hr/>
1384
- @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
1384
+ @ <p><input type="submit" name="submit" value="Apply Changes"></p>
13851385
@ </div></form>
13861386
db_end_transaction(0);
13871387
@ <script nonce="%h(style_nonce())">
13881388
@ (function(){
13891389
@ var w = document.getElementById('idsnd');
@@ -1412,11 +1412,11 @@
14121412
style_set_current_feature("setup");
14131413
style_header("Moderator For Wiki And Tickets");
14141414
db_begin_transaction();
14151415
@ <form action="%R/setup_modreq" method="post"><div>
14161416
login_insert_csrf_secret();
1417
- @ <hr />
1417
+ @ <hr>
14181418
onoff_attribute("Moderate ticket changes",
14191419
"modreq-tkt", "modreq-tkt", 0, 0);
14201420
@ <p>When enabled, any change to tickets is subject to the approval
14211421
@ by a ticket moderator - a user with the "q" or Mod-Tkt privilege.
14221422
@ Ticket changes enter the system and are shown locally, but are not
@@ -1423,11 +1423,11 @@
14231423
@ synced until they are approved. The moderator has the option to
14241424
@ delete the change rather than approve it. Ticket changes made by
14251425
@ a user who has the Mod-Tkt privilege are never subject to
14261426
@ moderation. (Property: "modreq-tkt")
14271427
@
1428
- @ <hr />
1428
+ @ <hr>
14291429
onoff_attribute("Moderate wiki changes",
14301430
"modreq-wiki", "modreq-wiki", 0, 0);
14311431
@ <p>When enabled, any change to wiki is subject to the approval
14321432
@ by a wiki moderator - a user with the "l" or Mod-Wiki privilege.
14331433
@ Wiki changes enter the system and are shown locally, but are not
@@ -1435,12 +1435,12 @@
14351435
@ delete the change rather than approve it. Wiki changes made by
14361436
@ a user who has the Mod-Wiki privilege are never subject to
14371437
@ moderation. (Property: "modreq-wiki")
14381438
@ </p>
14391439
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>
14421442
@ </div></form>
14431443
db_end_transaction(0);
14441444
style_finish_page();
14451445
14461446
}
@@ -1469,29 +1469,29 @@
14691469
14701470
style_set_current_feature("setup");
14711471
style_header("Edit Ad Unit");
14721472
@ <form action="%R/setup_adunit" method="post"><div>
14731473
login_insert_csrf_secret();
1474
- @ <b>Banner Ad-Unit:</b><br />
1474
+ @ <b>Banner Ad-Unit:</b><br>
14751475
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>
14781478
textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0);
1479
- @ <br />
1479
+ @ <br>
14801480
onoff_attribute("Omit ads to administrator",
14811481
"adunit-omit-if-admin", "oia", 0, 0);
1482
- @ <br />
1482
+ @ <br>
14831483
onoff_attribute("Omit ads to logged-in users",
14841484
"adunit-omit-if-user", "oiu", 0, 0);
1485
- @ <br />
1485
+ @ <br>
14861486
onoff_attribute("Temporarily disable all ads",
14871487
"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">
14911491
@ </div></form>
1492
- @ <hr />
1492
+ @ <hr>
14931493
@ <b>Ad-Unit Notes:</b><ul>
14941494
@ <li>Leave both Ad-Units blank to disable all advertising.
14951495
@ <li>The "Banner Ad-Unit" is used for wide pages.
14961496
@ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content.
14971497
@ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is
@@ -1651,12 +1651,12 @@
16511651
}
16521652
style_set_current_feature("setup");
16531653
style_header("Edit Project Logo And Background");
16541654
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
16551655
@ 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">
16581658
@ </p></blockquote>
16591659
@
16601660
@ <form action="%R/setup_logo" method="post"
16611661
@ enctype="multipart/form-data"><div>
16621662
@ <p>The logo is accessible to all users at this URL:
@@ -1665,22 +1665,22 @@
16651665
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
16661666
@ <a href="setup_skinedit?w=2">header setup</a>.
16671667
@ To change the logo image, use the following form:</p>
16681668
login_insert_csrf_secret();
16691669
@ Logo Image file:
1670
- @ <input type="file" name="logoim" size="60" accept="image/*" />
1670
+ @ <input type="file" name="logoim" size="60" accept="image/*">
16711671
@ <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>
16741674
@ <p>(Properties: "logo-image" and "logo-mimetype")
16751675
@ </div></form>
1676
- @ <hr />
1676
+ @ <hr>
16771677
@
16781678
@ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
16791679
@ and looks like this:</p>
16801680
@ <blockquote><p><img src="%R/background/%z(zBgMtime)" \
1681
- @ alt="background" border=1 />
1681
+ @ alt="background" border=1>
16821682
@ </p></blockquote>
16831683
@
16841684
@ <form action="%R/setup_logo" method="post"
16851685
@ enctype="multipart/form-data"><div>
16861686
@ <p>The background image is accessible to all users at this URL:
@@ -1689,22 +1689,22 @@
16891689
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
16901690
@ <a href="setup_skinedit?w=2">header setup</a>.
16911691
@ To change the background image, use the following form:</p>
16921692
login_insert_csrf_secret();
16931693
@ Background image file:
1694
- @ <input type="file" name="bgim" size="60" accept="image/*" />
1694
+ @ <input type="file" name="bgim" size="60" accept="image/*">
16951695
@ <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>
16981698
@ </div></form>
16991699
@ <p>(Properties: "background-image" and "background-mimetype")
1700
- @ <hr />
1700
+ @ <hr>
17011701
@
17021702
@ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b>
17031703
@ and looks like this:</p>
17041704
@ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \
1705
- @ alt="icon" border=1 />
1705
+ @ alt="icon" border=1>
17061706
@ </p></blockquote>
17071707
@
17081708
@ <form action="%R/setup_logo" method="post"
17091709
@ enctype="multipart/form-data"><div>
17101710
@ <p>The icon image is accessible to all users at this URL:
@@ -1713,17 +1713,17 @@
17131713
@ page depending on the web browser in use and the MIME-Types that it
17141714
@ supports for icon images.
17151715
@ To change the icon image, use the following form:</p>
17161716
login_insert_csrf_secret();
17171717
@ Icon image file:
1718
- @ <input type="file" name="iconim" size="60" accept="image/*" />
1718
+ @ <input type="file" name="iconim" size="60" accept="image/*">
17191719
@ <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>
17221722
@ </div></form>
17231723
@ <p>(Properties: "icon-image" and "icon-mimetype")
1724
- @ <hr />
1724
+ @ <hr>
17251725
@
17261726
@ <p><span class="note">Note:</span> Your browser has probably cached these
17271727
@ images, so you may need to press the Reload button before changes will
17281728
@ take effect. </p>
17291729
style_finish_page();
@@ -1804,12 +1804,12 @@
18041804
go = 1;
18051805
}
18061806
@
18071807
@ <form method="post" action="%R/admin_sql">
18081808
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>
18111811
@ <input type="submit" name="go" value="Run SQL">
18121812
@ <input type="submit" name="schema" value="Show Schema">
18131813
@ <input type="submit" name="tablelist" value="List Tables">
18141814
@ <input type="submit" name="configtab" value="CONFIG Table Query">
18151815
@ </form>
@@ -1827,11 +1827,11 @@
18271827
int rc;
18281828
const char *zTail;
18291829
int nCol;
18301830
int nRow = 0;
18311831
int i;
1832
- @ <hr />
1832
+ @ <hr>
18331833
login_verify_csrf_secret();
18341834
sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
18351835
search_sql_setup(g.db);
18361836
rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
18371837
if( rc!=SQLITE_OK ){
@@ -1913,19 +1913,19 @@
19131913
@ run by this page. If Tcl integration was enabled at compile-time and
19141914
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
19151915
@
19161916
@ <form method="post" action="%R/admin_th1">
19171917
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>
19201920
@ <input type="submit" name="go" value="Run TH1">
19211921
@ </form>
19221922
if( go ){
19231923
const char *zR;
19241924
int rc;
19251925
int n;
1926
- @ <hr />
1926
+ @ <hr>
19271927
login_verify_csrf_secret();
19281928
rc = Th_Eval(g.interp, 0, zQ, -1);
19291929
zR = Th_GetResult(g.interp, &n);
19301930
if( rc==TH_OK ){
19311931
@ <pre class="th1result">%h(zR)</pre>
@@ -2005,10 +2005,25 @@
20052005
@ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
20062006
}
20072007
style_finish_page();
20082008
}
20092009
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
+
20102025
/*
20112026
** WEBPAGE: srchsetup
20122027
**
20132028
** Configure the search engine. Requires Admin privilege.
20142029
*/
@@ -2024,11 +2039,11 @@
20242039
login_insert_csrf_secret();
20252040
@ <div style="text-align:center;font-weight:bold;">
20262041
@ Server-specific settings that affect the
20272042
@ <a href="%R/search">/search</a> webpage.
20282043
@ </div>
2029
- @ <hr />
2044
+ @ <hr>
20302045
textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
20312046
@ <p>The "Document Glob List" is a comma- or newline-separated list
20322047
@ of GLOB expressions that identify all documents within the source
20332048
@ tree that are to be searched when "Document Search" is enabled.
20342049
@ Some examples:
@@ -2040,51 +2055,53 @@
20402055
@ files.</tr>
20412056
@ <tr><td>*<td><td>Search all checked-in files</tr>
20422057
@ <tr><td><i>(blank)</i><td>
20432058
@ <td>Search nothing. (Disables document search).</tr>
20442059
@ </table>
2045
- @ <hr />
2060
+ @ <hr>
20462061
entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
20472062
@ <p>When searching documents, use the versions of the files found at the
20482063
@ type of the "Document Branch" branch. Recommended value: "trunk".
20492064
@ Document search is disabled if blank.
2050
- @ <hr />
2065
+ @ <hr>
20512066
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
2052
- @ <br />
2067
+ @ <br>
20532068
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
2054
- @ <br />
2069
+ @ <br>
20552070
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
2056
- @ <br />
2071
+ @ <br>
20572072
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
2058
- @ <br />
2073
+ @ <br>
20592074
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
2060
- @ <br />
2075
+ @ <br>
20612076
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>
20652080
if( P("fts0") ){
20662081
search_drop_index();
20672082
}else if( P("fts1") ){
2083
+ const char *zTokenizer = PD("ftstok","off");
2084
+ search_set_tokenizer(zTokenizer);
20682085
search_drop_index();
20692086
search_create_index();
20702087
search_fill_index();
20712088
search_update_index(search_restrict(SRCH_ALL));
20722089
}
20732090
if( search_index_exists() ){
20742091
@ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index.
20752092
@ The index helps search run faster, especially on large repositories,
20762093
@ but takes up space.</p>
2077
- onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
2094
+ select_fts_tokenizer();
20782095
@ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
20792096
@ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
20802097
style_submenu_element("FTS Index Debugging","%R/test-ftsdocs");
20812098
}else{
20822099
@ <p>The SQLite search index is disabled. All searching will be
20832100
@ a full-text scan. This usually works fine, but can be slow for
20842101
@ larger repositories.</p>
2085
- onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
2102
+ select_fts_tokenizer();
20862103
@ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
20872104
}
20882105
@ </div></form>
20892106
style_finish_page();
20902107
}
20912108
--- 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&nbsp;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: &lt;script&gt;, &lt;form&gt;, 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&nbsp;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: &lt;script&gt;, &lt;form&gt;, 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 @@
611611
@ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \
612612
@ name="id" value="%d(uid)"/>\
613613
@ </td>
614614
}else{
615615
@ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \
616
- @ value="0" /></td>
616
+ @ value="0"></td>
617617
}
618618
@ </tr>
619619
@ <tr>
620620
@ <td class="usetupEditLabel" id="sulgn">Login:</td>
621621
if( login_is_special(zLogin) ){
622622
@ <td><b>%h(zLogin)</b></td>
623623
}else{
624624
@ <td><input aria-labelledby="sulgn" type="text" name="login" \
625
- @ value="%h(zLogin)" />
625
+ @ value="%h(zLogin)">
626626
if( alert_tables_exist() ){
627627
int sid;
628628
sid = db_int(0, "SELECT subscriberId FROM subscriber"
629629
" WHERE suname=%Q", zLogin);
630630
if( sid>0 ){
@@ -644,82 +644,82 @@
644644
@ <td width="100%%">
645645
#define B(x) inherit[x]
646646
@ <div class="columns" style="column-width:13em;">
647647
@ <ul style="list-style-type: none;">
648648
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'])>
650650
@ Setup%s(B('s'))</label>
651651
}
652
- @ <li><label><input type="checkbox" name="aa"%s(oa['a']) />
652
+ @ <li><label><input type="checkbox" name="aa"%s(oa['a'])>
653653
@ 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'])>
655655
@ 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'])>
657657
@ Developer%s(B('v'))</label>
658658
#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'])>
660660
@ Delete%s(B('d'))</label>
661661
#endif
662
- @ <li><label><input type="checkbox" name="ae"%s(oa['e']) />
662
+ @ <li><label><input type="checkbox" name="ae"%s(oa['e'])>
663663
@ 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'])>
665665
@ 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'])>
667667
@ 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'])>
669669
@ 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'])>
671671
@ 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'])>
673673
@ 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'])>
675675
@ 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'])>
677677
@ 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'])>
679679
@ 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'])>
681681
@ 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'])>
683683
@ 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'])>
685685
@ 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'])>
687687
@ 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'])>
689689
@ 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'])>
691691
@ 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'])>
693693
@ 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'])>
695695
@ 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'])>
697697
@ 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'])>
699699
@ 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'])>
701701
@ 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'])>
703703
@ 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'])>
705705
@ 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'])>
707707
@ 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'])>
709709
@ 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'])>
711711
@ 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'])>
713713
@ 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'])>
715715
@ 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'])>
717717
@ 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'])>
719719
@ 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'])>
721721
@ Enable Debug%s(B('D'))</label>
722722
@ </ul></div>
723723
@ </td>
724724
@ </tr>
725725
@ <tr>
@@ -733,27 +733,27 @@
733733
@ <tr>
734734
@ <td align="right" id="supw">Password:</td>
735735
if( zPw[0] ){
736736
/* Obscure the password for all users */
737737
@ <td><input aria-labelledby="supw" type="password" autocomplete="off" \
738
- @ name="pw" value="**********" />
738
+ @ name="pw" value="**********">
739739
@ (Leave unchanged to retain password)</td>
740740
}else{
741741
/* Show an empty password as an empty input field */
742742
char *zRPW = fossil_random_password(12);
743743
@ <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>
745745
}
746746
@ </tr>
747747
}
748748
zGroup = login_group_name();
749749
if( zGroup ){
750750
@ <tr>
751751
@ <td valign="top" align="right">Scope:</td>
752752
@ <td valign="top">
753753
@ <input type="radio" name="all" checked value="0">
754
- @ Apply changes to this repository only.<br />
754
+ @ Apply changes to this repository only.<br>
755755
@ <input type="radio" name="all" value="1">
756756
@ Apply changes to all repositories in the "<b>%h(zGroup)</b>"
757757
@ login group.</td></tr>
758758
}
759759
if( !higherUser ){
760760
--- 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 @@
111111
}
112112
admin_log("Unshunned %Q", p);
113113
p += strlen(p)+1;
114114
}
115115
if( allExist ){
116
- @ <p class="noMoreShun">Artifact(s)<br />
116
+ @ <p class="noMoreShun">Artifact(s)<br>
117117
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>
119119
}
120120
@ are no longer being shunned.</p>
121121
}else{
122
- @ <p class="noMoreShun">Artifact(s)<br />
122
+ @ <p class="noMoreShun">Artifact(s)<br>
123123
for( p = zUuid ; *p ; p += strlen(p)+1 ){
124
- @ %s(p)<br />
124
+ @ %s(p)<br>
125125
}
126126
@ will no longer be shunned. But they may not exist in the repository.
127127
@ It may be necessary to rebuild the repository using the
128128
@ <b>fossil rebuild</b> command-line before the artifact content
129129
@ can pulled in from other repositories.</p>
@@ -149,13 +149,13 @@
149149
db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
150150
}
151151
admin_log("Shunned %Q", p);
152152
p += strlen(p)+1;
153153
}
154
- @ <p class="shunned">Artifact(s)<br />
154
+ @ <p class="shunned">Artifact(s)<br>
155155
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>
157157
}
158158
@ have been shunned. They will no longer be pushed.
159159
@ They will be removed from the repository the next time the repository
160160
@ is rebuilt using the <b>fossil rebuild</b> command-line</p>
161161
}
@@ -200,11 +200,11 @@
200200
}
201201
db_finalize(&q);
202202
}
203203
}
204204
@ </textarea>
205
- @ <input type="submit" name="add" value="Shun" />
205
+ @ <input type="submit" name="add" value="Shun">
206206
@ </div></form>
207207
@ </blockquote>
208208
@
209209
@ <a name="delshun"></a>
210210
@ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
@@ -227,11 +227,11 @@
227227
}
228228
db_finalize(&q);
229229
}
230230
}
231231
@ </textarea>
232
- @ <input type="submit" name="sub" value="Accept" />
232
+ @ <input type="submit" name="sub" value="Accept">
233233
@ </div></form>
234234
@ </blockquote>
235235
@
236236
@ <p>Press the Rebuild button below to rebuild the repository. The
237237
@ content of newly shunned artifacts is not purged until the repository
@@ -239,27 +239,27 @@
239239
@ two, so be patient after pressing the button.</p>
240240
@
241241
@ <blockquote>
242242
@ <form method="post" action="%R/%s(g.zPath)"><div>
243243
login_insert_csrf_secret();
244
- @ <input type="submit" name="rebuild" value="Rebuild" />
244
+ @ <input type="submit" name="rebuild" value="Rebuild">
245245
@ </div></form>
246246
@ </blockquote>
247247
@
248
- @ <hr /><p>Shunned Artifacts:</p>
248
+ @ <hr><p>Shunned Artifacts:</p>
249249
@ <blockquote><p>
250250
db_prepare(&q,
251251
"SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
252252
" FROM shun ORDER BY uuid");
253253
while( db_step(&q)==SQLITE_ROW ){
254254
const char *zUuid = db_column_text(&q, 0);
255255
int stillExists = db_column_int(&q, 1);
256256
cnt++;
257257
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>
259259
}else{
260
- @ <b>%s(zUuid)</b><br />
260
+ @ <b>%s(zUuid)</b><br>
261261
}
262262
}
263263
if( cnt==0 ){
264264
@ <i>no artifacts are shunned on this server</i>
265265
}
@@ -481,11 +481,11 @@
481481
@ <tr><th valign="top" align="right">Artifacts:</th>
482482
@ <td valign="top">
483483
}
484484
cnt++;
485485
@ <a href="%R/info/%s(zUuid)">%s(zUuid)</a>
486
- @ %h(zDesc) (size: %d(size))<br />
486
+ @ %h(zDesc) (size: %d(size))<br>
487487
}
488488
if( cnt>0 ){
489489
@ <p>
490490
if( db_exists(
491491
"SELECT 1 FROM blob WHERE rcvid=%d AND"
@@ -531,13 +531,13 @@
531531
@ <tr><th valign="top" align="right">Unversioned&nbsp;Files:</th>
532532
@ <td valign="top">
533533
}
534534
cnt++;
535535
if( isDeleted ){
536
- @ %h(zName) (deleted)<br />
536
+ @ %h(zName) (deleted)<br>
537537
}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>
539539
}
540540
}
541541
if( cnt>0 ){
542542
@ <p><form action='%R/rcvfrom'>
543543
@ <input type="hidden" name="rcvid" value='%d(rcvid)'>
544544
--- 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&nbsp;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&nbsp;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 @@
538538
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
539539
style_header("Confirm Custom Skin Delete");
540540
@ <form action="%R/setup_skin_admin" method="post"><div>
541541
@ <p>Deletion of a custom skin is a permanent action that cannot
542542
@ 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">
546546
login_insert_csrf_secret();
547547
@ </div></form>
548548
style_finish_page();
549549
db_end_transaction(1);
550550
return;
@@ -626,12 +626,12 @@
626626
if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){
627627
@ (Currently In Use)
628628
seenCurrent = 1;
629629
}else{
630630
@ <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">
633633
if( pAltSkin==&aBuiltinSkin[i] ){
634634
@ (Current override)
635635
}
636636
@ </form>
637637
}
@@ -858,22 +858,22 @@
858858
if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){
859859
db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile);
860860
}
861861
@ <textarea name="%s(zFile)" rows="10" cols="80">\
862862
@ %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">
865865
if( isRevert ){
866866
@ &larr; Press to complete reversion to "%s(zBasis)"
867867
}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)"'>
869869
}
870
- @ <hr />
870
+ @ <hr>
871871
@ Baseline: \
872872
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">
875875
if( P("diff")!=0 || P("sbsdiff")!=0 ){
876876
Blob from, to, out;
877877
DiffConfig DCfg;
878878
construct_diff_flags(1, &DCfg);
879879
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
880880
--- 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 @ &larr; 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 @ &larr; 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 @@
287287
void fossil_key(const char **pzKey, int *pnKey){
288288
char *zSavedKey = db_get_saved_encryption_key();
289289
char *zKey;
290290
size_t savedKeySize = db_get_saved_encryption_key_size();
291291
292
- if( zSavedKey==0 || savedKeySize==0 ) return;
292
+ if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return;
293293
zKey = (char*)malloc( savedKeySize );
294294
if( zKey ){
295295
memcpy(zKey, zSavedKey, savedKeySize);
296296
*pzKey = zKey;
297297
if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
298298
--- 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 @@
8181
case 'g':
8282
case 'G':
8383
zRealType = "g";
8484
rc = *zRealType;
8585
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;
8696
case 't':
8797
case 'T':
8898
zRealType = "t";
8999
rc = *zRealType;
90100
break;
@@ -103,19 +113,28 @@
103113
" (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))",
104114
P("from"), P("to"));
105115
}else{
106116
zTimeSpan = " 1";
107117
}
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' ){
109123
statsReportTimelineYFlag = zRealType;
110124
db_multi_exec("CREATE TEMP VIEW v_reports AS "
111125
"SELECT * FROM event WHERE (type GLOB %Q) AND %s",
112126
zRealType, zTimeSpan/*safe-for-%s*/);
113127
}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
+ );
117136
}
118137
return statsReportType = rc;
119138
}
120139
121140
/*
@@ -127,10 +146,14 @@
127146
static const char *stats_report_label_for_type(){
128147
assert( statsReportType && "Must call stats_report_init_view() first." );
129148
switch( statsReportType ){
130149
case 'c':
131150
return "check-ins";
151
+ case 'm':
152
+ return "merge check-ins";
153
+ case 'n':
154
+ return "non-merge check-ins";
132155
case 'e':
133156
return "technotes";
134157
case 'f':
135158
return "forum posts";
136159
case 'w':
@@ -143,48 +166,20 @@
143166
return "all types";
144167
}
145168
}
146169
147170
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
-
177171
/*
178172
** Implements the "byyear" and "bymonth" reports for /reports.
179173
** If includeMonth is true then it generates the "bymonth" report,
180174
** else the "byyear" report. If zUserName is not NULL then the report is
181175
** restricted to events created by the named user account.
182176
*/
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
+){
186181
Stmt query = empty_Stmt;
187182
int nRowNumber = 0; /* current TR number */
188183
int nEventTotal = 0; /* Total event count */
189184
int rowClass = 0; /* counter for alternating
190185
row colors */
@@ -197,32 +192,48 @@
197192
the per-year event totals */
198193
int nMaxEvents = 1; /* for calculating length of graph
199194
bars. */
200195
int iterations = 0; /* number of weeks/months we iterate
201196
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;
202204
stats_report_init_view();
203205
db_prepare(&query,
204206
"SELECT substr(date(mtime),1,%d) AS timeframe,"
205207
" count(*) AS eventCount"
206208
" FROM v_reports"
207209
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
208210
" GROUP BY timeframe"
209211
" ORDER BY timeframe DESC",
210
- includeMonth ? 7 : 4, zUserName);
212
+ nTFChar, zUserName);
211213
@ <h1>Timeline Events (%s(stats_report_label_for_type()))
212214
@ by year%s(includeMonth ? "/month" : "")
213215
if( zUserName ){
214216
@ for user %h(zUserName)
215217
}
216218
@ </h1>
217219
@ <table border='0' cellpadding='2' cellspacing='0' \
220
+ zCurrentTF = db_text(0, "SELECT substr(date(),1,%d)", nTFChar);
218221
if( !includeMonth ){
219222
@ class='statistics-report-table-events sortable' \
220223
@ data-column-types='tnx' data-init-sort='0'>
221224
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'));");
222229
}else{
223230
@ 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'));");
224235
}
225236
@ <thead>
226237
@ <th>%s(zTimeLabel)</th>
227238
@ <th>Events</th>
228239
@ <th width='90%%'><!-- relative commits graph --></th>
@@ -231,21 +242,26 @@
231242
Run the query twice. The first time we calculate the maximum
232243
number of events for a given row. Maybe someone with better SQL
233244
Fu can re-implement this with a single query.
234245
*/
235246
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
+ }
237253
if(nCount>nMaxEvents){
238254
nMaxEvents = nCount;
239255
}
240256
++iterations;
241257
}
242258
db_reset(&query);
243259
while( SQLITE_ROW == db_step(&query) ){
244260
const char *zTimeframe = db_column_text(&query, 0);
245261
const int nCount = db_column_int(&query, 1);
246
- int nSize = nCount
262
+ int nSize = (nCount>0 && nMaxEvents>0)
247263
? (int)(100 * nCount / nMaxEvents)
248264
: 1;
249265
showYearTotal = 0;
250266
if(!nSize) nSize = 1;
251267
if(includeMonth){
@@ -294,25 +310,30 @@
294310
}
295311
cgi_printf("'>%s</a>", zTimeframe);
296312
}
297313
@ </td><td>%d(nCount)</td>
298314
@ <td>
299
- @ <div class='statistics-report-graph-line'
300
- @ style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
327
+ @ <span class='statistics-report-graph-extra' \
328
+ @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329
+ }else{
330
+ @ <div class='statistics-report-graph-line' \
331
+ @ style='width:%d(nSize)%%;'>&nbsp;</div> \
332
+ }
301333
@ </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>
314335
/*
315336
Potential improvement: calculate the min/max event counts and
316337
use percent-based graph bars.
317338
*/
318339
}
@@ -327,12 +348,12 @@
327348
}
328349
@ </tbody></table>
329350
if(nEventTotal){
330351
const char *zAvgLabel = includeMonth ? "month" : "year";
331352
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)
334355
@ </div>
335356
}
336357
}
337358
338359
/*
@@ -354,11 +375,11 @@
354375
" GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;"
355376
);
356377
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
357378
@ <center><svg width=700 height=400>
358379
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
359
- @ </svg></centre><hr />
380
+ @ </svg></centre><hr>
360381
}
361382
style_table_sorter();
362383
@ <table class='statistics-report-table-events sortable' border='0' \
363384
@ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'>
364385
@ <thead><tr>
@@ -510,11 +531,11 @@
510531
, zUserName
511532
);
512533
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
513534
@ <center><svg width=700 height=400>
514535
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
515
- @ </svg></centre><hr />
536
+ @ </svg></centre><hr>
516537
}
517538
style_table_sorter();
518539
@ <table class='statistics-report-table-events sortable' border='0' \
519540
@ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'>
520541
@ <thead><tr>
@@ -587,11 +608,11 @@
587608
zUserName
588609
);
589610
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
590611
@ <center><svg width=700 height=400>
591612
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
592
- @ </svg></centre><hr />
613
+ @ </svg></centre><hr>
593614
}
594615
style_table_sorter();
595616
@ <table class='statistics-report-table-events sortable' border='0' \
596617
@ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'>
597618
@ <thead><tr>
@@ -629,12 +650,12 @@
629650
}
630651
631652
632653
/*
633654
** 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
636657
** created by the named user account.
637658
*/
638659
static void stats_report_year_weeks(const char *zUserName){
639660
const char *zYear = P("y"); /* Year for which report shown */
640661
Stmt q;
@@ -641,10 +662,13 @@
641662
int nMaxEvents = 1; /* max number of events for
642663
all rows. */
643664
int iterations = 0; /* # of active time periods. */
644665
int rowCount = 0;
645666
int total = 0;
667
+ char *zCurrentWeek; /* Current week number */
668
+ double rNowFraction = 0.0; /* Fraction of current week that has
669
+ ** passed */
646670
647671
stats_report_init_view();
648672
style_submenu_sql("y", "Year:",
649673
"WITH RECURSIVE a(b) AS ("
650674
" SELECT substr(date('now'),1,4) UNION ALL"
@@ -652,11 +676,11 @@
652676
" WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
653677
") SELECT b, b FROM a ORDER BY b DESC");
654678
if( zYear==0 || strlen(zYear)!=4 ){
655679
zYear = db_text("1970","SELECT substr(date('now'),1,4);");
656680
}
657
- cgi_printf("<br />");
681
+ cgi_printf("<br>\n");
658682
db_prepare(&q,
659683
"SELECT DISTINCT strftime('%%W',mtime) AS wk, "
660684
" count(*) AS n "
661685
" FROM v_reports "
662686
" WHERE %Q=substr(date(mtime),1,4) "
@@ -667,32 +691,45 @@
667691
@ for the calendar weeks of %h(zYear)
668692
if( zUserName ){
669693
@ for user %h(zUserName)
670694
}
671695
@ </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();
673704
cgi_printf("<table class='statistics-report-table-events sortable' "
674705
"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");
676707
cgi_printf("<thead><tr>"
677708
"<th>Week</th>"
678709
"<th>Events</th>"
679710
"<th width='90%%'><!-- relative commits graph --></th>"
680
- "</tr></thead>"
681
- "<tbody>");
711
+ "</tr></thead>\n"
712
+ "<tbody>\n");
682713
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
+ }
684721
if(nCount>nMaxEvents){
685722
nMaxEvents = nCount;
686723
}
687724
++iterations;
688725
}
689726
db_reset(&q);
690727
while( SQLITE_ROW == db_step(&q) ){
691728
const char *zWeek = db_column_text(&q,0);
692729
const int nCount = db_column_int(&q,1);
693
- int nSize = nCount
730
+ int nSize = (nCount>0 && nMaxEvents>0)
694731
? (int)(100 * nCount / nMaxEvents)
695732
: 0;
696733
if(!nSize) nSize = 1;
697734
total += nCount;
698735
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
@@ -704,22 +741,37 @@
704741
}
705742
cgi_printf("'>%s</a></td>",zWeek);
706743
707744
cgi_printf("<td>%d</td>",nCount);
708745
cgi_printf("<td>");
709
- if(nCount){
710
- cgi_printf("<div class='statistics-report-graph-line'"
711
- "style='width:%d%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
759
+ @ <span class='statistics-report-graph-extra' \
760
+ @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761
+ }else{
762
+ @ <div class='statistics-report-graph-line' \
763
+ @ style='width:%d(nSize)%%;'>&nbsp;</div> \
764
+ }
713765
}
714
- cgi_printf("</td></tr>");
766
+ cgi_printf("</td></tr>\n");
715767
}
716768
db_finalize(&q);
717769
cgi_printf("</tbody></table>");
718770
if(total){
719771
int nAvg = iterations ? (total/iterations) : 0;
720
- cgi_printf("<br /><div>Total events: %d<br />"
772
+ cgi_printf("<br><div>Total events: %d<br>"
721773
"Average per active week: %d</div>",
722774
total, nAvg);
723775
}
724776
}
725777
@@ -785,14 +837,29 @@
785837
**
786838
** Shows activity reports for the repository.
787839
**
788840
** Query Parameters:
789841
**
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
791851
** user=NAME Restricts statistics to the given user
792852
** 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)
794861
** Defaulting to all event types.
795862
**
796863
** The view-specific query parameters include:
797864
**
798865
** view=byweek:
@@ -822,10 +889,12 @@
822889
};
823890
static const char *const azType[] = {
824891
"a", "All Changes",
825892
"ci", "Check-ins",
826893
"f", "Forum Posts",
894
+ "m", "Merge check-ins",
895
+ "n", "Non-merge check-ins",
827896
"g", "Tags",
828897
"e", "Tech Notes",
829898
"t", "Tickets",
830899
"w", "Wiki"
831900
};
@@ -867,14 +936,14 @@
867936
}
868937
style_submenu_element("Stats", "%R/stat");
869938
style_header("Activity Reports");
870939
switch( eType ){
871940
case RPT_BYYEAR:
872
- stats_report_by_month_year(0, 0, zUserName);
941
+ stats_report_by_month_year(0, zUserName);
873942
break;
874943
case RPT_BYMONTH:
875
- stats_report_by_month_year(1, 0, zUserName);
944
+ stats_report_by_month_year(1, zUserName);
876945
break;
877946
case RPT_BYWEEK:
878947
stats_report_year_weeks(zUserName);
879948
break;
880949
default:
881950
--- 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)%%;'>&nbsp;</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%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
327 @ <span class='statistics-report-graph-extra' \
328 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329 }else{
330 @ <div class='statistics-report-graph-line' \
331 @ style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
759 @ <span class='statistics-report-graph-extra' \
760 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761 }else{
762 @ <div class='statistics-report-graph-line' \
763 @ style='width:%d(nSize)%%;'>&nbsp;</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 @@
648648
*/
649649
static const char zDfltHeader[] =
650650
@ <html>
651651
@ <head>
652652
@ <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">
655655
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
656656
@ <title>$<project_name>: $<title></title>
657657
@ <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">
660660
@ </head>
661661
@ <body class="$current_feature rpage-$requested_page cpage-$canonical_page">
662662
;
663663
664664
/*
@@ -817,20 +817,20 @@
817817
818818
cgi_destination(CGI_HEADER);
819819
820820
@ <!DOCTYPE html>
821821
822
- if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
822
+ if( g.thTrace ) Th_Trace("BEGIN_HEADER<br>\n", -1);
823823
824824
/* Generate the header up through the main menu */
825825
style_init_th1_vars(zTitle);
826826
if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){
827827
Th_Render(zDfltHeader);
828828
}
829
- if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
829
+ if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1);
830830
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);
832832
Th_Unstore("title"); /* Avoid collisions with ticket field names */
833833
cgi_destination(CGI_BODY);
834834
g.cgiOutput = 1;
835835
headerHasBeenGenerated = 1;
836836
sideboxUsed = 0;
@@ -1121,17 +1121,17 @@
11211121
/* Put the footer at the bottom of the page. */
11221122
zFooter = skin_get("footer");
11231123
if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
11241124
style_load_all_js_files();
11251125
}
1126
- if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
1126
+ if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1);
11271127
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);
11291129
11301130
/* Render trace log if TH1 tracing is enabled. */
11311131
if( g.thTrace ){
1132
- cgi_append_content("<span class=\"thTrace\"><hr />\n", -1);
1132
+ cgi_append_content("<span class=\"thTrace\"><hr>\n", -1);
11331133
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
11341134
cgi_append_content("</span>\n", -1);
11351135
}
11361136
11371137
/* Add document end mark if it was not in the footer */
@@ -1429,58 +1429,58 @@
14291429
style_submenu_element("Stats", "%R/stat");
14301430
}
14311431
14321432
if( isAuth ){
14331433
#if !defined(_WIN32)
1434
- @ uid=%d(getuid()), gid=%d(getgid())<br />
1434
+ @ uid=%d(getuid()), gid=%d(getgid())<br>
14351435
#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>
14441444
if( g.zLocalRoot ){
1445
- @ g.zLocalRoot = %h(g.zLocalRoot)<br />
1445
+ @ g.zLocalRoot = %h(g.zLocalRoot)<br>
14461446
}else{
1447
- @ g.zLocalRoot = <i>none</i><br />
1447
+ @ g.zLocalRoot = <i>none</i><br>
14481448
}
14491449
if( g.nRequest ){
1450
- @ g.nRequest = %d(g.nRequest)<br />
1450
+ @ g.nRequest = %d(g.nRequest)<br>
14511451
}
14521452
if( g.nPendingRequest>1 ){
1453
- @ g.nPendingRequest = %d(g.nPendingRequest)<br />
1453
+ @ g.nPendingRequest = %d(g.nPendingRequest)<br>
14541454
}
1455
- @ capabilities = %s(find_capabilities(zCap))<br />
1455
+ @ capabilities = %s(find_capabilities(zCap))<br>
14561456
if( zCap[0] ){
1457
- @ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
1457
+ @ anonymous-adds = %s(find_anon_capabilities(zCap))<br>
14581458
}
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>
14611461
#ifndef _WIN32
1462
- @ RSS = %.2f(fossil_rss()/1000000.0) MB</br />
1462
+ @ RSS = %.2f(fossil_rss()/1000000.0) MB</br>
14631463
#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>
14661466
if( g.perm.Admin ){
14671467
int k;
14681468
for(k=0; g.argvOrig[k]; k++){
14691469
Blob t;
14701470
blob_init(&t, 0, 0);
14711471
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>
14731473
blob_zero(&t);
14741474
}
14751475
}
1476
- @ <hr />
1476
+ @ <hr>
14771477
P("HTTP_USER_AGENT");
14781478
P("SERVER_SOFTWARE");
14791479
cgi_print_all(showAll, 0);
14801480
if( showAll && blob_size(&g.httpHeader)>0 ){
1481
- @ <hr />
1481
+ @ <hr>
14821482
@ <pre>
14831483
@ %h(blob_str(&g.httpHeader))
14841484
@ </pre>
14851485
}
14861486
}
14871487
--- 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 @@
886886
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
887887
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
888888
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
889889
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890890
db_finalize(&q);
891
- @ <br />
891
+ @ <br>
892892
style_finish_page();
893893
}
894894
895895
/*
896896
** Returns true if the given blob.rid value has the given tag ID
897897
--- 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 @@
886886
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
887887
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
888888
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
889889
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
890890
db_finalize(&q);
891
- @ <br />
891
+ @ <br>
892892
style_finish_page();
893893
}
894894
895895
/*
896896
** Returns true if the given blob.rid value has the given tag ID
897897
--- 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 @@
808808
zKey = blob_str(&cacheKey);
809809
etag_check(ETAG_HASH, zKey);
810810
811811
if( P("debug")!=0 ){
812812
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>
815815
if( zInclude ){
816
- @ zInclude = "%h(zInclude)"<br />
816
+ @ zInclude = "%h(zInclude)"<br>
817817
}
818818
if( zExclude ){
819
- @ zExclude = "%h(zExclude)"<br />
819
+ @ zExclude = "%h(zExclude)"<br>
820820
}
821821
@ zKey = "%h(zKey)"
822822
style_finish_page();
823823
return;
824824
}
@@ -826,11 +826,11 @@
826826
style_header("Tarball Download");
827827
@ <form action='%R/tarball/%h(zName).tar.gz'>
828828
cgi_query_parameters_to_hidden();
829829
@ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content
830830
@ of check-in <b>%h(zRid)</b>:
831
- @ <input type="submit" value="Download" />
831
+ @ <input type="submit" value="Download">
832832
@ </form>
833833
style_finish_page();
834834
return;
835835
}
836836
blob_zero(&tarball);
837837
--- 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
+21
--- src/th.c
+++ src/th.c
@@ -1269,10 +1269,31 @@
12691269
return TH_ERROR;
12701270
}
12711271
12721272
return Th_SetResult(interp, pValue->zData, pValue->nData);
12731273
}
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
+}
12741295
12751296
/*
12761297
** Return true if variable (zVar, nVar) exists.
12771298
*/
12781299
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
12791300
--- 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
+13
--- src/th.h
+++ src/th.h
@@ -56,10 +56,23 @@
5656
int Th_GetVar(Th_Interp *, const char *, int);
5757
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
5858
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
5959
int Th_UnsetVar(Th_Interp *, const char *, int);
6060
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
+
6174
typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
6275
6376
/*
6477
** Register new commands.
6578
*/
6679
--- 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 @@
290290
if( argc<2 || argc>3 ){
291291
return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
292292
}
293293
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
294294
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);
296296
}
297297
return rc;
298298
}
299299
300300
/*
@@ -320,11 +320,11 @@
320320
}
321321
buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1;
322322
Th_SetResultInt(g.interp, buul);
323323
if(argc>1){
324324
if( g.thTrace ){
325
- Th_Trace("enable_htmlify {%.*s} -> %d<br />\n",
325
+ Th_Trace("enable_htmlify {%.*s} -> %d<br>\n",
326326
argl[1],argv[1],buul);
327327
}
328328
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul);
329329
if(!rc){
330330
if(buul){
@@ -412,11 +412,11 @@
412412
*/
413413
static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
414414
int savedEnable = enableOutput;
415415
enableOutput = 1;
416416
if( forceCgi || g.cgiOutput ){
417
- sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0);
417
+ sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0);
418418
}
419419
sendText(pOut,"ERROR: ", -1, 0);
420420
sendText(pOut,(char*)z, n, 1);
421421
sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
422422
enableOutput = savedEnable;
@@ -791,11 +791,11 @@
791791
Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]);
792792
}
793793
rc = login_has_capability((char*)argv[i],argl[i],*(int*)p);
794794
}
795795
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);
797797
Th_Free(interp, zCapList);
798798
}
799799
Th_SetResultInt(interp, rc);
800800
return TH_OK;
801801
}
@@ -908,11 +908,11 @@
908908
}
909909
}
910910
if( !match ) rc = 0;
911911
}
912912
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);
914914
}
915915
Th_SetResultInt(interp, rc);
916916
return TH_OK;
917917
}
918918
@@ -1027,11 +1027,11 @@
10271027
#endif
10281028
else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
10291029
rc = 1;
10301030
}
10311031
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);
10331033
}
10341034
Th_SetResultInt(interp, rc);
10351035
return TH_OK;
10361036
}
10371037
@@ -1058,11 +1058,11 @@
10581058
if( g.tcl.interp ){
10591059
rc = 1;
10601060
}
10611061
#endif
10621062
if( g.thTrace ){
1063
- Th_Trace("[tclReady] => %d<br />\n", rc);
1063
+ Th_Trace("[tclReady] => %d<br>\n", rc);
10641064
}
10651065
Th_SetResultInt(interp, rc);
10661066
return TH_OK;
10671067
}
10681068
@@ -1087,11 +1087,11 @@
10871087
}
10881088
for(i=0; rc==0 && i<argl[1]; i++){
10891089
rc = login_has_capability((char*)&argv[1][i],1,0);
10901090
}
10911091
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);
10931093
}
10941094
Th_SetResultInt(interp, rc);
10951095
return TH_OK;
10961096
}
10971097
@@ -1953,17 +1953,17 @@
19531953
const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
19541954
int szVal = sqlite3_column_bytes(pStmt, i);
19551955
Th_SetVar(interp, zCol, szCol, zVal, szVal);
19561956
}
19571957
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]);
19591959
}
19601960
res = Th_Eval(interp, 0, argv[2], argl[2]);
19611961
if( g.thTrace ){
19621962
int nTrRes;
19631963
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",
19651965
Th_ReturnCodeName(res, 0), nTrRes, zTrRes);
19661966
}
19671967
if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK;
19681968
}
19691969
rc = sqlite3_finalize(pStmt);
@@ -2013,11 +2013,11 @@
20132013
}else{
20142014
Th_SetResult(interp, 0, 0);
20152015
rc = TH_OK;
20162016
}
20172017
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 " : "",
20192019
argl[nArg], argv[nArg], rc);
20202020
}
20212021
return rc;
20222022
}
20232023
@@ -2374,11 +2374,11 @@
23742374
{"verifyLogin", verifyLoginCmd, 0},
23752375
{"wiki", wikiCmd, (void*)&aFlags[0]},
23762376
{0, 0, 0}
23772377
};
23782378
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);
23802380
}
23812381
if( needConfig ){
23822382
/*
23832383
** This function uses several settings which may be defined in the
23842384
** repository and/or the global configuration. Since the caller
@@ -2394,11 +2394,11 @@
23942394
Th_Vtab *pVtab = 0;
23952395
#if defined(TH_MEMDEBUG)
23962396
if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
23972397
pVtab = &vtab;
23982398
if( g.thTrace ){
2399
- Th_Trace("th1-init MEMDEBUG ENABLED<br />\n");
2399
+ Th_Trace("th1-init MEMDEBUG ENABLED<br>\n");
24002400
}
24012401
}
24022402
#endif
24032403
g.interp = Th_CreateInterp(pVtab);
24042404
created = 1;
@@ -2435,11 +2435,11 @@
24352435
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
24362436
sendError(0,zResult, nResult, 0);
24372437
}
24382438
}
24392439
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,
24412441
Th_ReturnCodeName(rc, 0));
24422442
}
24432443
}
24442444
g.th1Flags &= ~TH_INIT_MASK;
24452445
g.th1Flags |= (flags & TH_INIT_MASK);
@@ -2451,11 +2451,11 @@
24512451
*/
24522452
void Th_MaybeStore(const char *zName, const char *zValue){
24532453
Th_FossilInit(TH_INIT_DEFAULT);
24542454
if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){
24552455
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);
24572457
}
24582458
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
24592459
}
24602460
}
24612461
@@ -2464,11 +2464,11 @@
24642464
*/
24652465
void Th_Store(const char *zName, const char *zValue){
24662466
Th_FossilInit(TH_INIT_DEFAULT);
24672467
if( zValue ){
24682468
if( g.thTrace ){
2469
- Th_Trace("set %h {%h}<br />\n", zName, zValue);
2469
+ Th_Trace("set %h {%h}<br>\n", zName, zValue);
24702470
}
24712471
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
24722472
}
24732473
}
24742474
@@ -2507,11 +2507,11 @@
25072507
int i;
25082508
for(i=0; i<nList; i++){
25092509
Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1);
25102510
}
25112511
if( g.thTrace ){
2512
- Th_Trace("set %h {%h}<br />\n", zName, zValue);
2512
+ Th_Trace("set %h {%h}<br>\n", zName, zValue);
25132513
}
25142514
Th_SetVar(g.interp, zName, -1, zValue, nValue);
25152515
Th_Free(g.interp, zValue);
25162516
}
25172517
}
@@ -2525,11 +2525,11 @@
25252525
Th_FossilInit(TH_INIT_DEFAULT);
25262526
blob_zero(&value);
25272527
blob_appendf(&value, "%d", iValue);
25282528
zValue = blob_str(&value);
25292529
if( g.thTrace ){
2530
- Th_Trace("set %h {%h}<br />\n", zName, zValue);
2530
+ Th_Trace("set %h {%h}<br>\n", zName, zValue);
25312531
}
25322532
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
25332533
blob_reset(&value);
25342534
}
25352535
@@ -2673,11 +2673,11 @@
26732673
** do not want to skip executing essential commands unless the called command
26742674
** (i.e. "command_hook") explicitly forbids this by successfully returning
26752675
** TH_BREAK or TH_CONTINUE.
26762676
*/
26772677
if( g.thTrace ){
2678
- Th_Trace("[command_hook {%h}] => %h<br />\n", zName,
2678
+ Th_Trace("[command_hook {%h}] => %h<br>\n", zName,
26792679
Th_ReturnCodeName(rc, 0));
26802680
}
26812681
/*
26822682
** Does our call to Th_FossilInit() result in opening a database? If so,
26832683
** clean it up now. This is very important because some commands do not
@@ -2705,11 +2705,11 @@
27052705
Th_Store("cmd_name", zName);
27062706
Th_StoreList("cmd_args", g.argv, g.argc);
27072707
Th_StoreInt("cmd_flags", cmdFlags);
27082708
rc = Th_Eval(g.interp, 0, "command_notify", -1);
27092709
if( g.thTrace ){
2710
- Th_Trace("[command_notify {%h}] => %h<br />\n", zName,
2710
+ Th_Trace("[command_notify {%h}] => %h<br>\n", zName,
27112711
Th_ReturnCodeName(rc, 0));
27122712
}
27132713
/*
27142714
** Does our call to Th_FossilInit() result in opening a database? If so,
27152715
** clean it up now. This is very important because some commands do not
@@ -2760,11 +2760,11 @@
27602760
** do not want to skip processing essential web pages unless the called
27612761
** command (i.e. "webpage_hook") explicitly forbids this by successfully
27622762
** returning TH_BREAK or TH_CONTINUE.
27632763
*/
27642764
if( g.thTrace ){
2765
- Th_Trace("[webpage_hook {%h}] => %h<br />\n", zName,
2765
+ Th_Trace("[webpage_hook {%h}] => %h<br>\n", zName,
27662766
Th_ReturnCodeName(rc, 0));
27672767
}
27682768
/*
27692769
** Does our call to Th_FossilInit() result in opening a database? If so,
27702770
** clean it up now. This is very important because some commands do not
@@ -2792,11 +2792,11 @@
27922792
Th_Store("web_name", zName);
27932793
Th_StoreList("web_args", g.argv, g.argc);
27942794
Th_StoreInt("web_flags", cmdFlags);
27952795
rc = Th_Eval(g.interp, 0, "webpage_notify", -1);
27962796
if( g.thTrace ){
2797
- Th_Trace("[webpage_notify {%h}] => %h<br />\n", zName,
2797
+ Th_Trace("[webpage_notify {%h}] => %h<br>\n", zName,
27982798
Th_ReturnCodeName(rc, 0));
27992799
}
28002800
/*
28012801
** Does our call to Th_FossilInit() result in opening a database? If so,
28022802
** clean it up now. This is very important because some commands do not
@@ -2875,17 +2875,17 @@
28752875
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
28762876
sendText(pOut,z, i, 0);
28772877
z += i+5;
28782878
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
28792879
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);
28812881
}
28822882
rc = Th_Eval(g.interp, 0, (const char*)z, i);
28832883
if( g.thTrace ){
28842884
int nTrRes;
28852885
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",
28872887
Th_ReturnCodeName(rc, 0), nTrRes, zTrRes);
28882888
}
28892889
if( rc!=TH_OK ) break;
28902890
z += i;
28912891
if( z[0] ){ z += 6; }
28922892
--- 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 @@
144144
db_bind_int(&q, "$rid", rid);
145145
res = db_step(&q)==SQLITE_ROW;
146146
db_reset(&q);
147147
return res;
148148
}
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
+
149167
150168
/*
151169
** Output a timeline in the web format given a query. The query
152170
** should return these columns:
153171
**
@@ -278,11 +296,11 @@
278296
@ </td></tr>
279297
pendingEndTr = 0;
280298
}
281299
if( fossil_strcmp(zType,"div")==0 ){
282300
if( !prevWasDivider ){
283
- @ <tr><td colspan="3"><hr class="timelineMarker" /></td></tr>
301
+ @ <tr><td colspan="3"><hr class="timelineMarker"></td></tr>
284302
}
285303
prevWasDivider = 1;
286304
continue;
287305
}
288306
prevWasDivider = 0;
@@ -1619,10 +1637,11 @@
16191637
** to see all changes for the current year.
16201638
** days=N Show events over the previous N days
16211639
** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
16221640
** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
16231641
** bisect Show the check-ins that are in the current bisect
1642
+** oldestfirst Show events oldest first.
16241643
** showid Show RIDs
16251644
** showsql Show the SQL text
16261645
**
16271646
** p= and d= can appear individually or together. If either p= or d=
16281647
** appear, then u=, y=, a=, and b= are ignored.
@@ -2554,21 +2573,31 @@
25542573
}
25552574
if( zUser ){
25562575
int n = db_int(0,"SELECT count(*) FROM event"
25572576
" WHERE user=%Q OR euser=%Q", zUser, zUser);
25582577
if( n<=nEntry ){
2559
- zCirca = zBefore = zAfter = 0;
25602578
nEntry = -1;
25612579
}
25622580
blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)",
25632581
zUser, zUser);
25642582
zThisUser = zUser;
25652583
}
25662584
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
+ }
25702599
}
25712600
rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
25722601
rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
25732602
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
25742603
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
@@ -2682,19 +2711,19 @@
26822711
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
26832712
}
26842713
addFileGlobDescription(zChng, &desc);
26852714
if( rAfter>0.0 ){
26862715
if( rBefore>0.0 ){
2687
- blob_appendf(&desc, " occurring between %h and %h.<br />",
2716
+ blob_appendf(&desc, " occurring between %h and %h.<br>",
26882717
zAfter, zBefore);
26892718
}else{
2690
- blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
2719
+ blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
26912720
}
26922721
}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);
26942723
}else if( rCirca>0.0 ){
2695
- blob_appendf(&desc, " occurring around %h.<br />", zCirca);
2724
+ blob_appendf(&desc, " occurring around %h.<br>", zCirca);
26962725
}
26972726
if( zSearch ){
26982727
blob_appendf(&desc, " matching \"%h\"", zSearch);
26992728
}
27002729
if( g.perm.Hyperlink ){
@@ -2767,11 +2796,15 @@
27672796
if( useDividers && zMark && zMark[0] ){
27682797
double r = symbolic_name_to_mtime(zMark, 0);
27692798
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
27702799
}
27712800
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
+ }
27732806
if( fossil_islower(desc.aData[0]) ){
27742807
desc.aData[0] = fossil_toupper(desc.aData[0]);
27752808
}
27762809
if( zBrName ){
27772810
if( !PB("nowiki")
27782811
--- 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 @@
764764
}
765765
}
766766
if( !showTimeline && g.perm.Hyperlink ){
767767
style_submenu_element("Timeline", "%R/info/%T", zUuid);
768768
}
769
- if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
769
+ if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
770770
ticket_init();
771771
initializeVariablesFromCGI();
772772
getAllTicketFields();
773773
initializeVariablesFromDb();
774774
zScript = ticket_viewpage_code();
775775
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);
777777
safe_html_context(DOCSRC_TICKET);
778778
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);
780780
781781
zFullName = db_text(0,
782782
"SELECT tkt_uuid FROM ticket"
783783
" WHERE tkt_uuid GLOB '%q*'", zUuid);
784784
if( zFullName ){
785
- attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
785
+ attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>");
786786
}
787787
788788
style_finish_page();
789789
}
790790
@@ -807,11 +807,11 @@
807807
808808
if( argc!=3 ){
809809
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
810810
}
811811
if( g.thTrace ){
812
- Th_Trace("append_field %#h {%#h}<br />\n",
812
+ Th_Trace("append_field %#h {%#h}<br>\n",
813813
argl[1], argv[1], argl[2], argv[2]);
814814
}
815815
for(idx=0; idx<nField; idx++){
816816
if( memcmp(aField[idx].zName, argv[1], argl[1])==0
817817
&& aField[idx].zName[argl[1]]==0 ){
@@ -967,15 +967,15 @@
967967
@ <div style="color:blue">
968968
@ <p>Ticket artifact that would have been submitted:</p>
969969
@ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
970970
@ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
971971
@ </div>
972
- @ <hr />
972
+ @ <hr>
973973
}else{
974974
if( g.thTrace ){
975975
Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
976
- "}<br />\n",
976
+ "}<br>\n",
977977
blob_str(&tktchng));
978978
}
979979
ticket_put(&tktchng, zUuid, aUsed, needMod);
980980
rc = ticket_change(zUuid);
981981
}
@@ -997,11 +997,11 @@
997997
** when you press submit - it just prints the ticket artifact at the
998998
** top of the screen.
999999
*/
10001000
void tktnew_page(void){
10011001
const char *zScript;
1002
- char *zEmail = 0, *zNewUuid = 0;
1002
+ char *zNewUuid = 0;
10031003
int uid;
10041004
10051005
login_check_credentials();
10061006
if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
10071007
if( P("cancel") ){
@@ -1008,11 +1008,11 @@
10081008
cgi_redirect("home");
10091009
}
10101010
style_set_current_feature("tkt");
10111011
style_header("New Ticket");
10121012
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);
10141014
ticket_init();
10151015
initializeVariablesFromCGI();
10161016
getAllTicketFields();
10171017
initializeVariablesFromDb();
10181018
if( g.zPath[0]=='d' ) showAllFields();
@@ -1021,31 +1021,36 @@
10211021
if( P("date_override") && g.perm.Setup ){
10221022
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
10231023
}
10241024
zScript = ticket_newpage_code();
10251025
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);
10271030
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);
10301034
if( zEmail ){
10311035
Th_Store("private_contact", zEmail);
1036
+ fossil_free(zEmail);
10321037
}
10331038
}
10341039
}
10351040
Th_Store("login", login_name());
10361041
Th_Store("date", db_text(0, "SELECT datetime('now')"));
10371042
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
10381043
(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);
10401045
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
10411046
cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
10421047
return;
10431048
}
10441049
captcha_generate(0);
10451050
@ </form>
1046
- if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
1051
+ if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
10471052
style_finish_page();
10481053
}
10491054
10501055
/*
10511056
** WEBPAGE: tktedit
@@ -1092,32 +1097,32 @@
10921097
@ <span class="tktError">%d(nRec) tickets begin with:
10931098
@ "%h(zName)"</span>
10941099
style_finish_page();
10951100
return;
10961101
}
1097
- if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
1102
+ if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
10981103
ticket_init();
10991104
getAllTicketFields();
11001105
initializeVariablesFromCGI();
11011106
initializeVariablesFromDb();
11021107
if( g.zPath[0]=='d' ) showAllFields();
11031108
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)">
11051110
login_insert_csrf_secret();
11061111
zScript = ticket_editpage_code();
11071112
Th_Store("login", login_name());
11081113
Th_Store("date", db_text(0, "SELECT datetime('now')"));
11091114
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
11101115
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);
11121117
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
11131118
cgi_redirect(mprintf("%R/tktview/%s", zName));
11141119
return;
11151120
}
11161121
captcha_generate(0);
11171122
@ </form>
1118
- if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
1123
+ if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
11191124
style_finish_page();
11201125
}
11211126
11221127
/*
11231128
** Check the ticket table schema in zSchema to see if it appears to
11241129
--- 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 @@
764764
}
765765
}
766766
if( !showTimeline && g.perm.Hyperlink ){
767767
style_submenu_element("Timeline", "%R/info/%T", zUuid);
768768
}
769
- if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
769
+ if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
770770
ticket_init();
771771
initializeVariablesFromCGI();
772772
getAllTicketFields();
773773
initializeVariablesFromDb();
774774
zScript = ticket_viewpage_code();
775775
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);
777777
safe_html_context(DOCSRC_TICKET);
778778
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);
780780
781781
zFullName = db_text(0,
782782
"SELECT tkt_uuid FROM ticket"
783783
" WHERE tkt_uuid GLOB '%q*'", zUuid);
784784
if( zFullName ){
785
- attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
785
+ attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>");
786786
}
787787
788788
style_finish_page();
789789
}
790790
@@ -807,11 +807,11 @@
807807
808808
if( argc!=3 ){
809809
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
810810
}
811811
if( g.thTrace ){
812
- Th_Trace("append_field %#h {%#h}<br />\n",
812
+ Th_Trace("append_field %#h {%#h}<br>\n",
813813
argl[1], argv[1], argl[2], argv[2]);
814814
}
815815
for(idx=0; idx<nField; idx++){
816816
if( memcmp(aField[idx].zName, argv[1], argl[1])==0
817817
&& aField[idx].zName[argl[1]]==0 ){
@@ -967,15 +967,15 @@
967967
@ <div style="color:blue">
968968
@ <p>Ticket artifact that would have been submitted:</p>
969969
@ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
970970
@ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
971971
@ </div>
972
- @ <hr />
972
+ @ <hr>
973973
}else{
974974
if( g.thTrace ){
975975
Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
976
- "}<br />\n",
976
+ "}<br>\n",
977977
blob_str(&tktchng));
978978
}
979979
ticket_put(&tktchng, zUuid, aUsed, needMod);
980980
rc = ticket_change(zUuid);
981981
}
@@ -997,11 +997,11 @@
997997
** when you press submit - it just prints the ticket artifact at the
998998
** top of the screen.
999999
*/
10001000
void tktnew_page(void){
10011001
const char *zScript;
1002
- char *zEmail = 0, *zNewUuid = 0;
1002
+ char *zNewUuid = 0;
10031003
int uid;
10041004
10051005
login_check_credentials();
10061006
if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
10071007
if( P("cancel") ){
@@ -1008,11 +1008,11 @@
10081008
cgi_redirect("home");
10091009
}
10101010
style_set_current_feature("tkt");
10111011
style_header("New Ticket");
10121012
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);
10141014
ticket_init();
10151015
initializeVariablesFromCGI();
10161016
getAllTicketFields();
10171017
initializeVariablesFromDb();
10181018
if( g.zPath[0]=='d' ) showAllFields();
@@ -1021,31 +1021,36 @@
10211021
if( P("date_override") && g.perm.Setup ){
10221022
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
10231023
}
10241024
zScript = ticket_newpage_code();
10251025
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);
10271030
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);
10301034
if( zEmail ){
10311035
Th_Store("private_contact", zEmail);
1036
+ fossil_free(zEmail);
10321037
}
10331038
}
10341039
}
10351040
Th_Store("login", login_name());
10361041
Th_Store("date", db_text(0, "SELECT datetime('now')"));
10371042
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
10381043
(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);
10401045
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
10411046
cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
10421047
return;
10431048
}
10441049
captcha_generate(0);
10451050
@ </form>
1046
- if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
1051
+ if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
10471052
style_finish_page();
10481053
}
10491054
10501055
/*
10511056
** WEBPAGE: tktedit
@@ -1092,32 +1097,32 @@
10921097
@ <span class="tktError">%d(nRec) tickets begin with:
10931098
@ "%h(zName)"</span>
10941099
style_finish_page();
10951100
return;
10961101
}
1097
- if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
1102
+ if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
10981103
ticket_init();
10991104
getAllTicketFields();
11001105
initializeVariablesFromCGI();
11011106
initializeVariablesFromDb();
11021107
if( g.zPath[0]=='d' ) showAllFields();
11031108
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)">
11051110
login_insert_csrf_secret();
11061111
zScript = ticket_editpage_code();
11071112
Th_Store("login", login_name());
11081113
Th_Store("date", db_text(0, "SELECT datetime('now')"));
11091114
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
11101115
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);
11121117
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
11131118
cgi_redirect(mprintf("%R/tktview/%s", zName));
11141119
return;
11151120
}
11161121
captcha_generate(0);
11171122
@ </form>
1118
- if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
1123
+ if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
11191124
style_finish_page();
11201125
}
11211126
11221127
/*
11231128
** Check the ticket table schema in zSchema to see if it appears to
11241129
--- 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 @@
156156
@ <form action="%R/%s(g.zPath)" method="post"><div>
157157
login_insert_csrf_secret();
158158
@ <p>%s(zDesc)</p>
159159
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
160160
@ <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">
164164
@ </p></blockquote>
165165
@ </div></form>
166
- @ <hr />
166
+ @ <hr>
167167
@ <h2>Default %s(zTitle)</h2>
168168
@ <blockquote><pre>
169169
@ %h(zDfltValue)
170170
@ </pre></blockquote>
171171
style_finish_page();
@@ -324,12 +324,12 @@
324324
@ </th1>
325325
@ <h1 style="text-align: center;">Enter A New Ticket</h1>
326326
@ <table cellpadding="5">
327327
@ <tr>
328328
@ <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>">
331331
@ </td>
332332
@ </tr>
333333
@
334334
@ <tr>
335335
@ <td align="right">Type:</td>
@@ -338,11 +338,11 @@
338338
@ </tr>
339339
@
340340
@ <tr>
341341
@ <td align="right">Version:</td>
342342
@ <td align="left">
343
-@ <input type="text" name="foundin" size="20" value="$<foundin>" />
343
+@ <input type="text" name="foundin" size="20" value="$<foundin>">
344344
@ </td>
345345
@ <td align="left">In what version or build number do you observe
346346
@ the problem?</td>
347347
@ </tr>
348348
@
@@ -354,12 +354,11 @@
354354
@ </tr>
355355
@
356356
@ <tr>
357357
@ <td align="right">EMail:</td>
358358
@ <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">
361360
@ </td>
362361
@ <td align="left"><u>Not publicly visible</u>
363362
@ Used by developers to contact you with questions.</td>
364363
@ </tr>
365364
@
@@ -368,19 +367,19 @@
368367
@ Enter a detailed description of the problem.
369368
@ For code defects, be sure to provide details on exactly how
370369
@ the problem can be reproduced. Provide as much detail as
371370
@ possible. Format:
372371
@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1>
373
-@ <br />
372
+@ <br>
374373
@ <th1>set nline [linecount $comment 50 10]</th1>
375374
@ <textarea name="icomment" cols="80" rows="$nline"
376
-@ wrap="virtual" class="wikiedit">$<icomment></textarea><br />
375
+@ wrap="virtual" class="wikiedit">$<icomment></textarea><br>
377376
@ </tr>
378377
@
379378
@ <th1>enable_output [info exists preview]</th1>
380379
@ <tr><td colspan="3">
381
-@ Description Preview:<br /><hr />
380
+@ Description Preview:<br><hr>
382381
@ <th1>
383382
@ if {$mutype eq "Wiki"} {
384383
@ wiki $icomment
385384
@ } elseif {$mutype eq "Plain Text"} {
386385
@ set r [randhex]
@@ -392,33 +391,33 @@
392391
@ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>"
393392
@ } else {
394393
@ wiki "<nowiki>$icomment\n</nowiki>"
395394
@ }
396395
@ </th1>
397
-@ <hr /></td></tr>
396
+@ <hr></td></tr>
398397
@ <th1>enable_output 1</th1>
399398
@
400399
@ <tr>
401400
@ <td><td align="left">
402
-@ <input type="submit" name="preview" value="Preview" />
401
+@ <input type="submit" name="preview" value="Preview">
403402
@ </td>
404403
@ <td align="left">See how the description will appear after formatting.</td>
405404
@ </tr>
406405
@
407406
@ <th1>enable_output [info exists preview]</th1>
408407
@ <tr>
409408
@ <td><td align="left">
410
-@ <input type="submit" name="submit" value="Submit" />
409
+@ <input type="submit" name="submit" value="Submit">
411410
@ </td>
412411
@ <td align="left">After filling in the information above, press this
413412
@ button to create the new ticket</td>
414413
@ </tr>
415414
@ <th1>enable_output 1</th1>
416415
@
417416
@ <tr>
418417
@ <td><td align="left">
419
-@ <input type="submit" name="cancel" value="Cancel" />
418
+@ <input type="submit" name="cancel" value="Cancel">
420419
@ </td>
421420
@ <td>Abandon and forget this ticket</td>
422421
@ </tr>
423422
@ </table>
424423
;
@@ -532,11 +531,11 @@
532531
@ mimetype as xmimetype, icomment AS xcomment,
533532
@ username AS xusername
534533
@ FROM ticketchng
535534
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
536535
@ if {$seenRow} {
537
-@ html "<hr />\n"
536
+@ html "<hr>\n"
538537
@ } else {
539538
@ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n"
540539
@ html "<tr><td colspan='5' class='tktDspValue'>\n"
541540
@ set seenRow 1
542541
@ }
@@ -616,11 +615,11 @@
616615
@ set preview 1
617616
@ }
618617
@ </th1>
619618
@ <table cellpadding="5">
620619
@ <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">
622621
@ </td></tr>
623622
@
624623
@ <tr><td class="tktDspLabel">Status:</td><td>
625624
@ <th1>combobox status $status_choices 1</th1>
626625
@ </td></tr>
@@ -646,30 +645,30 @@
646645
@ </td></tr>
647646
@
648647
@ <th1>enable_output [hascap e]</th1>
649648
@ <tr><td class="tktDspLabel">Contact:</td><td>
650649
@ <input type="text" name="private_contact" size="40"
651
-@ value="$<private_contact>" />
650
+@ value="$<private_contact>">
652651
@ </td></tr>
653652
@ <th1>enable_output 1</th1>
654653
@
655654
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
656
-@ <input type="text" name="foundin" size="50" value="$<foundin>" />
655
+@ <input type="text" name="foundin" size="50" value="$<foundin>">
657656
@ </td></tr>
658657
@
659658
@ <tr><td colspan="2">
660659
@ Append Remark with format
661660
@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1>
662661
@ from
663
-@ <input type="text" name="username" value="$<username>" size="30" />:<br />
662
+@ <input type="text" name="username" value="$<username>" size="30">:<br>
664663
@ <textarea name="icomment" cols="80" rows="15"
665664
@ wrap="virtual" class="wikiedit">$<icomment></textarea>
666665
@ </td></tr>
667666
@
668667
@ <th1>enable_output [info exists preview]</th1>
669668
@ <tr><td colspan="2">
670
-@ Description Preview:<br /><hr />
669
+@ Description Preview:<br><hr>
671670
@ <th1>
672671
@ if {$mutype eq "Wiki"} {
673672
@ wiki $icomment
674673
@ } elseif {$mutype eq "Plain Text"} {
675674
@ set r [randhex]
@@ -681,33 +680,33 @@
681680
@ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>"
682681
@ } else {
683682
@ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>"
684683
@ }
685684
@ </th1>
686
-@ <hr />
685
+@ <hr>
687686
@ </td></tr>
688687
@ <th1>enable_output 1</th1>
689688
@
690689
@ <tr>
691690
@ <td align="right">
692
-@ <input type="submit" name="preview" value="Preview" />
691
+@ <input type="submit" name="preview" value="Preview">
693692
@ </td>
694693
@ <td align="left">See how the description will appear after formatting.</td>
695694
@ </tr>
696695
@
697696
@ <th1>enable_output [info exists preview]</th1>
698697
@ <tr>
699698
@ <td align="right">
700
-@ <input type="submit" name="submit" value="Submit" />
699
+@ <input type="submit" name="submit" value="Submit">
701700
@ </td>
702701
@ <td align="left">Apply the changes shown above</td>
703702
@ </tr>
704703
@ <th1>enable_output 1</th1>
705704
@
706705
@ <tr>
707706
@ <td align="right">
708
-@ <input type="submit" name="cancel" value="Cancel" />
707
+@ <input type="submit" name="cancel" value="Cancel">
709708
@ </td>
710709
@ <td>Abandon this edit</td>
711710
@ </tr>
712711
@
713712
@ </table>
@@ -912,36 +911,36 @@
912911
style_header("Ticket Display On Timelines");
913912
db_begin_transaction();
914913
@ <form action="%R/tktsetup_timeline" method="post"><div>
915914
login_insert_csrf_secret();
916915
917
- @ <hr />
916
+ @ <hr>
918917
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t",
919918
"title", 0);
920919
@ <p>An SQL expression in a query against the TICKET table that will
921920
@ return the title of the ticket for display purposes.
922921
@ (Property: ticket-title-expr)</p>
923922
924
- @ <hr />
923
+ @ <hr>
925924
entry_attribute("Ticket Status", 40, "ticket-status-column", "s",
926925
"status", 0);
927926
@ <p>The name of the column in the TICKET table that contains the ticket
928927
@ status in human-readable form. Case sensitive.
929928
@ (Property: ticket-status-column)</p>
930929
931
- @ <hr />
930
+ @ <hr>
932931
entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
933932
"status='Closed'", 0);
934933
@ <p>An SQL expression that evaluates to true in a TICKET table query if
935934
@ the ticket is closed.
936935
@ (Property: ticket-closed-expr)</p>
937936
938
- @ <hr />
937
+ @ <hr>
939938
@ <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">
942941
@ </p>
943942
@ </div></form>
944943
db_end_transaction(0);
945944
style_finish_page();
946945
947946
}
948947
--- 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&nbsp;Found&nbsp;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&nbsp;Found&nbsp;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 @@
652652
*/
653653
void ensure_empty_dirs_created(int clearDirTable){
654654
char *zEmptyDirs = db_get("empty-dirs", 0);
655655
if( zEmptyDirs!=0 ){
656656
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];
667661
char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir);
668662
switch( file_isdir(zPath, RepoFILE) ){
669663
case 0: { /* doesn't exist */
670664
fossil_free(zPath);
671665
zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir);
@@ -688,14 +682,12 @@
688682
fossil_warning("file %s found, but a directory is required "
689683
"by empty-dirs setting", zDir);
690684
}
691685
}
692686
fossil_free(zPath);
693
- blob_reset(&dirName);
694687
}
695
- blob_reset(&dirsList);
696
- fossil_free(zEmptyDirs);
688
+ glob_free(pGlob);
697689
}
698690
}
699691
700692
/*
701693
** Get the manifest record for a given revision, or the current check-out if
702694
--- 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 @@
755755
if( skip>0 || cnt>n ){
756756
style_submenu_element("All", "%R/access_log?n=10000000");
757757
}
758758
@ </tbody></table>
759759
db_finalize(&q);
760
- @ <hr />
760
+ @ <hr>
761761
@ <form method="post" action="%R/access_log">
762762
@ <label><input type="checkbox" name="delold">
763763
@ Delete all but the most recent 200 entries</input></label>
764764
@ <input type="submit" name="deloldbtn" value="Delete"></input>
765765
@ </form>
766766
--- 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
--- src/vfile.c
+++ src/vfile.c
@@ -255,10 +255,13 @@
255255
}
256256
#ifndef _WIN32
257257
if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){
258258
/* Changing to a symlink takes priority over all other change types. */
259259
chnged = 7;
260
+ }else if( origPerm==PERM_LNK && currentPerm!=PERM_LNK ){
261
+ /* Ditto, other direction */
262
+ chnged = 9;
260263
}else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){
261264
/* Confirm metadata change types. */
262265
if( origPerm==currentPerm ){
263266
chnged = 0;
264267
}else if( currentPerm==PERM_EXE ){
265268
--- 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 @@
612612
wiki_render_by_mimetype(&wiki, zMimetype);
613613
blob_reset(&wiki);
614614
}
615615
manifest_destroy(pWiki);
616616
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'>"
618618
"Attachments</a>:</h2><ul>",
619619
zPageName);
620620
attachment_list(zPageName, zLabel);
621621
fossil_free(zLabel);
622622
document_emit_js(/*for optional pikchr support*/);
@@ -1536,14 +1536,14 @@
15361536
wiki_standard_submenu(W_ALL_BUT(W_NEW));
15371537
@ <p>Rules for wiki page names:</p>
15381538
well_formed_wiki_name_rules();
15391539
form_begin(0, "%R/wikinew");
15401540
@ <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>
15421542
@ %z(href("%R/markup_help"))Markup style</a>:
15431543
mimetype_option_menu("text/x-markdown", "mimetype");
1544
- @ <br /><input type="submit" value="Create" />
1544
+ @ <br><input type="submit" value="Create">
15451545
@ </p></form>
15461546
if( zName[0] ){
15471547
@ <p><span class="wikiError">
15481548
@ "%h(zName)" is not a valid wiki page name!</span></p>
15491549
}
@@ -1563,16 +1563,16 @@
15631563
zDate = db_text(0, "SELECT datetime('now')");
15641564
zRemark = PD("r","");
15651565
zUser = PD("u",g.zLogin);
15661566
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
15671567
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",
15691569
zId, zDate, login_name());
15701570
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
15711571
blob_appendf(p, " (claiming to be %h)", zUser);
15721572
}
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);
15741574
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
15751575
blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name());
15761576
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
15771577
blob_appendf(p, " (claiming to be %h)", zUser);
15781578
}
@@ -1685,31 +1685,31 @@
16851685
}
16861686
if( !isSandbox && P("preview")!=0 ){
16871687
Blob preview;
16881688
blob_zero(&preview);
16891689
appendRemark(&preview, zMimetype);
1690
- @ Preview:<hr />
1690
+ @ Preview:<hr>
16911691
safe_html_context(DOCSRC_WIKI);
16921692
wiki_render_by_mimetype(&preview, zMimetype);
1693
- @ <hr />
1693
+ @ <hr>
16941694
blob_reset(&preview);
16951695
}
16961696
zUser = PD("u", g.zLogin);
16971697
form_begin(0, "%R/wikiappend");
16981698
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)">
17011701
@ 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>
17031703
zFormat = mimetype_common_name(zMimetype);
1704
- @ Comment to append (formatted as %s(zFormat)):<br />
1704
+ @ Comment to append (formatted as %s(zFormat)):<br>
17051705
@ <textarea name="r" class="wikiedit" cols="80"
17061706
@ 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">
17111711
captcha_generate(0);
17121712
@ </form>
17131713
manifest_destroy(pWiki);
17141714
style_finish_page();
17151715
}
@@ -1750,12 +1750,12 @@
17501750
" ORDER BY event.mtime DESC",
17511751
zPageName
17521752
);
17531753
@ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2>
17541754
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">
17571757
@ </form>
17581758
@ <style> .wh-clickable { cursor: pointer; } </style>
17591759
@ <div class="brlist">
17601760
@ <table>
17611761
@ <thead><tr>
@@ -1790,11 +1790,11 @@
17901790
/* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */
17911791
@ <td>%s(zAge)</td>
17921792
fossil_free(zAge);
17931793
@ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td>
17941794
@ <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>
17961796
if( showRid ){
17971797
@ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td>
17981798
}
17991799
@ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td>
18001800
@ </tr>
18011801
--- 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 @@
612612
wiki_render_by_mimetype(&wiki, zMimetype);
613613
blob_reset(&wiki);
614614
}
615615
manifest_destroy(pWiki);
616616
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'>"
618618
"Attachments</a>:</h2><ul>",
619619
zPageName);
620620
attachment_list(zPageName, zLabel);
621621
fossil_free(zLabel);
622622
document_emit_js(/*for optional pikchr support*/);
@@ -1536,14 +1536,14 @@
15361536
wiki_standard_submenu(W_ALL_BUT(W_NEW));
15371537
@ <p>Rules for wiki page names:</p>
15381538
well_formed_wiki_name_rules();
15391539
form_begin(0, "%R/wikinew");
15401540
@ <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>
15421542
@ %z(href("%R/markup_help"))Markup style</a>:
15431543
mimetype_option_menu("text/x-markdown", "mimetype");
1544
- @ <br /><input type="submit" value="Create" />
1544
+ @ <br><input type="submit" value="Create">
15451545
@ </p></form>
15461546
if( zName[0] ){
15471547
@ <p><span class="wikiError">
15481548
@ "%h(zName)" is not a valid wiki page name!</span></p>
15491549
}
@@ -1563,16 +1563,16 @@
15631563
zDate = db_text(0, "SELECT datetime('now')");
15641564
zRemark = PD("r","");
15651565
zUser = PD("u",g.zLogin);
15661566
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
15671567
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",
15691569
zId, zDate, login_name());
15701570
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
15711571
blob_appendf(p, " (claiming to be %h)", zUser);
15721572
}
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);
15741574
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
15751575
blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name());
15761576
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
15771577
blob_appendf(p, " (claiming to be %h)", zUser);
15781578
}
@@ -1685,31 +1685,31 @@
16851685
}
16861686
if( !isSandbox && P("preview")!=0 ){
16871687
Blob preview;
16881688
blob_zero(&preview);
16891689
appendRemark(&preview, zMimetype);
1690
- @ Preview:<hr />
1690
+ @ Preview:<hr>
16911691
safe_html_context(DOCSRC_WIKI);
16921692
wiki_render_by_mimetype(&preview, zMimetype);
1693
- @ <hr />
1693
+ @ <hr>
16941694
blob_reset(&preview);
16951695
}
16961696
zUser = PD("u", g.zLogin);
16971697
form_begin(0, "%R/wikiappend");
16981698
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)">
17011701
@ 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>
17031703
zFormat = mimetype_common_name(zMimetype);
1704
- @ Comment to append (formatted as %s(zFormat)):<br />
1704
+ @ Comment to append (formatted as %s(zFormat)):<br>
17051705
@ <textarea name="r" class="wikiedit" cols="80"
17061706
@ 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">
17111711
captcha_generate(0);
17121712
@ </form>
17131713
manifest_destroy(pWiki);
17141714
style_finish_page();
17151715
}
@@ -1750,12 +1750,12 @@
17501750
" ORDER BY event.mtime DESC",
17511751
zPageName
17521752
);
17531753
@ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2>
17541754
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">
17571757
@ </form>
17581758
@ <style> .wh-clickable { cursor: pointer; } </style>
17591759
@ <div class="brlist">
17601760
@ <table>
17611761
@ <thead><tr>
@@ -1790,11 +1790,11 @@
17901790
/* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */
17911791
@ <td>%s(zAge)</td>
17921792
fossil_free(zAge);
17931793
@ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td>
17941794
@ <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>
17961796
if( showRid ){
17971797
@ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td>
17981798
}
17991799
@ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td>
18001800
@ </tr>
18011801
--- 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 @@
195195
MARKUP_CODE,
196196
MARKUP_COL,
197197
MARKUP_COLGROUP,
198198
MARKUP_DD,
199199
MARKUP_DEL,
200
+ MARKUP_DETAILS,
200201
MARKUP_DFN,
201202
MARKUP_DIV,
202203
MARKUP_DL,
203204
MARKUP_DT,
204205
MARKUP_EM,
@@ -229,10 +230,11 @@
229230
MARKUP_SMALL,
230231
MARKUP_SPAN,
231232
MARKUP_STRIKE,
232233
MARKUP_STRONG,
233234
MARKUP_SUB,
235
+ MARKUP_SUMMARY,
234236
MARKUP_SUP,
235237
MARKUP_TABLE,
236238
MARKUP_TBODY,
237239
MARKUP_TD,
238240
MARKUP_TFOOT,
@@ -286,14 +288,13 @@
286288
AMSK_TITLE},
287289
{ "abbr", MARKUP_ABBR, MUTYPE_FONT,
288290
AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE },
289291
{ "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE },
290292
{ "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK,
291
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
293
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
292294
{ "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK,
293
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
294
-
295
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
295296
{ "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE },
296297
{ "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE },
297298
{ "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE },
298299
{ "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR },
299300
{ "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE },
@@ -303,10 +304,12 @@
303304
AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE },
304305
{ "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK,
305306
AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE},
306307
{ "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE },
307308
{ "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE },
309
+ { "details", MARKUP_DETAILS, MUTYPE_BLOCK,
310
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
308311
{ "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE },
309312
{ "div", MARKUP_DIV, MUTYPE_BLOCK,
310313
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
311314
{ "dl", MARKUP_DL, MUTYPE_LIST,
312315
AMSK_COMPACT|AMSK_STYLE },
@@ -313,12 +316,11 @@
313316
{ "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE },
314317
{ "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE },
315318
{ "font", MARKUP_FONT, MUTYPE_FONT,
316319
AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE },
317320
{ "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK,
318
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
319
-
321
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
320322
{ "h1", MARKUP_H1, MUTYPE_BLOCK,
321323
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
322324
{ "h2", MARKUP_H2, MUTYPE_BLOCK,
323325
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
324326
{ "h3", MARKUP_H3, MUTYPE_BLOCK,
@@ -327,14 +329,12 @@
327329
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
328330
{ "h5", MARKUP_H5, MUTYPE_BLOCK,
329331
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
330332
{ "h6", MARKUP_H6, MUTYPE_BLOCK,
331333
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
332
-
333334
{ "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK,
334
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
335
-
335
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
336336
{ "hr", MARKUP_HR, MUTYPE_SINGLE,
337337
AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH|
338338
AMSK_STYLE|AMSK_CLASS },
339339
{ "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE },
340340
{ "img", MARKUP_IMG, MUTYPE_SINGLE,
@@ -343,11 +343,11 @@
343343
{ "ins", MARKUP_INS, MUTYPE_FONT, AMSK_STYLE },
344344
{ "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE },
345345
{ "li", MARKUP_LI, MUTYPE_LI,
346346
AMSK_TYPE|AMSK_VALUE|AMSK_STYLE },
347347
{ "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK,
348
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
348
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
349349
{ "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 },
350350
{ "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 },
351351
{ "ol", MARKUP_OL, MUTYPE_LIST,
352352
AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE },
353353
{ "p", MARKUP_P, MUTYPE_BLOCK,
@@ -354,17 +354,19 @@
354354
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
355355
{ "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE },
356356
{ "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE },
357357
{ "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE },
358358
{ "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK,
359
- AMSK_ID|AMSK_CLASS|AMSK_STYLE },
359
+ AMSK_ID|AMSK_CLASS|AMSK_STYLE },
360360
{ "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE },
361361
{ "span", MARKUP_SPAN, MUTYPE_BLOCK,
362362
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
363363
{ "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE },
364364
{ "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE },
365365
{ "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE },
366
+ { "summary", MARKUP_SUMMARY, MUTYPE_BLOCK,
367
+ AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
366368
{ "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE },
367369
{ "table", MARKUP_TABLE, MUTYPE_TABLE,
368370
AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
369371
AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS|
370372
AMSK_STYLE },
371373
--- 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 @@
625625
blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
626626
}
627627
#if USE_SEE
628628
zSavedKey = db_get_saved_encryption_key();
629629
savedKeySize = db_get_saved_encryption_key_size();
630
- if( zSavedKey!=0 && savedKeySize>0 ){
630
+ if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){
631631
blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
632632
zSavedKey, savedKeySize);
633633
}
634634
#endif
635635
if( WSAStartup(MAKEWORD(2,0), &wd) ){
636636
--- 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 @@
28572857
g.clockSkewSeen = 1;
28582858
}
28592859
28602860
fossil_force_newline();
28612861
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
+ }
28652875
}
28662876
if( syncFlags & SYNC_VERBOSE ){
28672877
fossil_print(
28682878
"Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd);
28692879
}
28702880
--- 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 @@
6161
zButton = "Synchronize";
6262
zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.",
6363
g.url.canonical);
6464
}
6565
@ <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>
6767
@ This may be useful when testing the various transfer scripts.</p>
6868
@ <p>You can use the <code>http -async</code> command in your scripts, but
6969
@ make sure the <code>th1-uri-regexp</code> setting is set first.</p>
7070
if( zWarning ){
7171
@
@@ -73,11 +73,11 @@
7373
free(zWarning);
7474
}
7575
@
7676
@ <form method="post" action="%R/%s(g.zPath)"><div>
7777
login_insert_csrf_secret();
78
- @ <input type="submit" name="sync" value="%h(zButton)" />
78
+ @ <input type="submit" name="sync" value="%h(zButton)">
7979
@ </div></form>
8080
@
8181
if( P("sync") ){
8282
user_select();
8383
url_enable_proxy(0);
@@ -139,17 +139,17 @@
139139
@ <form action="%R/%s(g.zPath)" method="post"><div>
140140
login_insert_csrf_secret();
141141
@ <p>%s(zDesc)</p>
142142
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
143143
@ <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">
147147
@ </p>
148148
@ </div></form>
149149
if ( zDfltValue ){
150
- @ <hr />
150
+ @ <hr>
151151
@ <h2>Default %s(zTitle)</h2>
152152
@ <blockquote><pre>
153153
@ %h(zDfltValue)
154154
@ </pre></blockquote>
155155
}
156156
--- 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 @@
991991
etag_check(ETAG_HASH, zKey);
992992
993993
style_set_current_feature("zip");
994994
if( P("debug")!=0 ){
995995
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>
998998
if( zInclude ){
999
- @ zInclude = "%h(zInclude)"<br />
999
+ @ zInclude = "%h(zInclude)"<br>
10001000
}
10011001
if( zExclude ){
1002
- @ zExclude = "%h(zExclude)"<br />
1002
+ @ zExclude = "%h(zExclude)"<br>
10031003
}
10041004
@ zKey = "%h(zKey)"
10051005
style_finish_page();
10061006
return;
10071007
}
@@ -1009,11 +1009,11 @@
10091009
style_header("%s Archive Download", zType);
10101010
@ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'>
10111011
cgi_query_parameters_to_hidden();
10121012
@ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b>
10131013
@ holding the content of check-in <b>%h(zRid)</b>:
1014
- @ <input type="submit" value="Download" />
1014
+ @ <input type="submit" value="Download">
10151015
@ </form>
10161016
style_finish_page();
10171017
return;
10181018
}
10191019
blob_zero(&zip);
10201020
--- 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
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -556,24 +556,24 @@
556556
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
557557
558558
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
559559
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
560560
561
-writeln {
561
+writeln [string map [list <<<NEXT_LINE>>> \\] {
562562
$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
563563
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
564564
565565
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
566566
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
567567
568568
$(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>>>
575575
--minify 0
576576
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
577577
wasm: $(SRCDIR_extsrc)/pikchr.js
578578
579579
#
@@ -580,11 +580,11 @@
580580
# The list of all the targets that do not correspond to real files. This stops
581581
# 'make' from getting confused when someone makes an error in a rule.
582582
#
583583
584584
.PHONY: all install test clean
585
-}
585
+}]
586586
587587
close $output_file
588588
#
589589
# End of the main.mk output
590590
##############################################################################
@@ -2049,13 +2049,19 @@
20492049
foreach s [lsort $extra_files] {
20502050
writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@"
20512051
set redir {>>}
20522052
}
20532053
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
+
20542060
writeln ""
20552061
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)"
20572063
writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n"
20582064
writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\""
20592065
writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n"
20602066
}
20612067
20622068
--- 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
--- tools/mkindex.c
+++ tools/mkindex.c
@@ -59,10 +59,11 @@
5959
** New arguments may be added in future releases that set additional
6060
** bits in the eCmdFlags field.
6161
**
6262
** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING:
6363
** 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.)
6465
**
6566
** Multiple COMMAND: entries can be attached to the same command, thus
6667
** creating multiple aliases for that command. Similarly, multiple
6768
** WEBPAGE: entries can be attached to the same webpage function, to give
6869
** that page aliases.
6970
--- 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 @@
565565
$(SRCDIR)/../skins/blitz/css.txt \
566566
$(SRCDIR)/../skins/blitz/details.txt \
567567
$(SRCDIR)/../skins/blitz/footer.txt \
568568
$(SRCDIR)/../skins/blitz/header.txt \
569569
$(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 \
574570
$(SRCDIR)/../skins/darkmode/css.txt \
575571
$(SRCDIR)/../skins/darkmode/details.txt \
576572
$(SRCDIR)/../skins/darkmode/footer.txt \
577573
$(SRCDIR)/../skins/darkmode/header.txt \
578574
$(SRCDIR)/../skins/default/css.txt \
579575
--- 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
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -1313,11 +1313,11 @@
13131313
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c"
13141314
13151315
"$(OX)\browse_.c" : "$(SRCDIR)\browse.c"
13161316
"$(OBJDIR)\translate$E" $** > $@
13171317
1318
-"$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h"
1318
+"$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h"
13191319
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c"
13201320
13211321
"$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c"
13221322
"$(OBJDIR)\translate$E" $** > $@
13231323
@@ -1451,11 +1451,11 @@
14511451
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c"
14521452
14531453
"$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c"
14541454
"$(OBJDIR)\translate$E" $** > $@
14551455
1456
-"$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h"
1456
+"$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h"
14571457
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c"
14581458
14591459
"$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c"
14601460
"$(OBJDIR)\translate$E" $** > $@
14611461
14621462
--- 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 @@
11
<title>How CGI Works In Fossil</title>
22
<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
44
generating dynamic web content. This article gives a quick background on how
55
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
78
information on the CGI protocol so that you can better understand what
89
is going on behind the scenes. If you just want to set up Fossil
910
as a CGI server, see the [./server/ | Fossil Server Setup] page. Or
1011
if you want to development CGI-based extensions to Fossil, see
1112
the [./serverext.wiki|CGI Server Extensions] page.
1213
</blockquote>
1314
<h2>A Quick Review Of CGI</h2><blockquote>
14
-<p>
15
+
1516
An HTTP request is a block of text that is sent by a client application
1617
(usually a web browser) and arrives at the web server over a network
1718
connection. The HTTP request contains a URL that describes the information
1819
being requested. The URL in the HTTP request is typically the same URL
1920
that appears in the URL bar at the top of the web browser that is making
@@ -20,33 +21,33 @@
2021
the request. The URL might contain a "?" character followed
2122
query parameters. The HTTP will usually also contain other information
2223
such as the name of the application that made the request, whether or
2324
not the requesting application can accept a compressed reply, POST
2425
parameters from forms, and so forth.
25
-<p>
26
+
2627
The job of the web server is to interpret the HTTP request and formulate
2728
an appropriate reply.
2829
The web server is free to interpret the HTTP request in any way it wants.
2930
But most web servers follow a similar pattern, described below.
3031
(Note: details may vary from one web server to another.)
31
-<p>
32
+
3233
Suppose the filename component of the URL in the HTTP request looks like this:
3334
<blockquote><b>/one/two/timeline/four</b></blockquote>
3435
Most web servers will search their content area for files that match
3536
some prefix of the URL. The search starts with <b>/one</b>, then goes to
3637
<b>/one/two</b>, then <b>/one/two/timeline</b>, and finally
3738
<b>/one/two/timeline/four</b> is checked. The search stops at the first
3839
match.
39
-<p>
40
+
4041
Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an
4142
ordinary file in the content area, then that file is returned as static
4243
content. The "<b>/timeline/four</b>" suffix is silently ignored.
43
-<p>
44
+
4445
If <b>/one/two</b> is a CGI script (or program), then the web server
4546
executes the <b>/one/two</b> script. The output generated by
4647
the script is collected and repackaged as the HTTP reply.
47
-<p>
48
+
4849
Before executing the CGI script, the web server will set up various
4950
environment variables with information useful to the CGI script:
5051
<table border=1 cellpadding=5>
5152
<tr><th>Environment<br>Variable<th>Meaning
5253
<tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0"
@@ -59,21 +60,21 @@
5960
<td>The suffix of the URL beyond the name of the CGI script.
6061
In this example: "timeline/four".
6162
<tr><td>QUERY_STRING
6263
<td>The query string that follows the "?" in the URL, if there is one.
6364
</table>
64
-<p>
65
+
6566
There are other CGI environment variables beyond those listed above.
6667
Many Fossil servers implement the
6768
[https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env]
6869
webpage that shows some of the CGI environment
6970
variables that Fossil pays attention to.
70
-<p>
71
+
7172
In addition to setting various CGI environment variables, if the HTTP
7273
request contains POST content, then the web server relays the POST content
7374
to standard input of the CGI script.
74
-<p>
75
+
7576
In summary, the task of the
7677
CGI script is to read the various CGI environment variables and
7778
the POST content on standard input (if any), figure out an appropriate
7879
reply, then write that reply on standard output.
7980
The web server will read the output from the CGI script, reformat it
@@ -80,11 +81,11 @@
8081
into an appropriate HTTP reply, and relay the result back to the
8182
requesting application.
8283
The CGI script exits as soon as it generates a single reply.
8384
The web server will (usually) persist and handle multiple HTTP requests,
8485
but a CGI script handles just one HTTP request and then exits.
85
-<p>
86
+
8687
The above is a rough outline of how CGI works.
8788
There are many details omitted from this brief discussion.
8889
See other on-line CGI tutorials for further information.
8990
</blockquote>
9091
<h2>How Fossil Acts As A CGI Program</h2>
@@ -103,33 +104,33 @@
103104
shebang with a single argument that is the full pathname of the script
104105
itself.
105106
In our example, the interpreter is Fossil, and the argument might
106107
be something like "/var/www/cgi-bin/one/two" (depending on how your
107108
particular web server is configured).
108
-<p>
109
+
109110
The Fossil program that is run as the script interpreter
110111
is the same Fossil that runs when
111112
you type ordinary Fossil commands like "fossil sync" or "fossil commit".
112113
But in this case, as soon as it launches, the Fossil program
113114
recognizes that the GATEWAY_INTERFACE environment variable is
114115
set to "CGI/1.0" and it therefore knows that it is being used as
115116
CGI rather than as an ordinary command-line tool, and behaves accordingly.
116
-<p>
117
+
117118
When Fossil recognizes that it is being run as CGI, it opens and reads
118119
the file identified by its sole argument (the file named by
119120
<code>argv&#91;1&#93;</code>). In our example, the second line of that file
120121
tells Fossil the location of the repository it will be serving.
121122
Fossil then starts looking at the CGI environment variables to figure
122123
out what web page is being requested, generates that one web page,
123124
then exits.
124
-<p>
125
+
125126
Usually, the webpage being requested is the first term of the
126127
PATH_INFO environment variable. (Exceptions to this rule are noted
127128
in the sequel.) For our example, the first term of PATH_INFO
128129
is "timeline", which means that Fossil will generate
129130
the [/help?cmd=/timeline|/timeline] webpage.
130
-<p>
131
+
131132
With Fossil, terms of PATH_INFO beyond the webpage name are converted into
132133
the "name" query parameter. Hence, the following two URLs mean
133134
exactly the same thing to Fossil:
134135
<ol type='A'>
135136
<li> [https://fossil-scm.org/home/info/c14ecc43]
@@ -149,11 +150,11 @@
149150
using a single CGI script.
150151
On a website that wants to serve multiple repositories, one could
151152
simply create multiple CGI scripts, one script for each repository.
152153
But it is also possible to serve multiple Fossil repositories from
153154
a single CGI script.
154
-<p>
155
+
155156
If the CGI script for Fossil contains a "directory:" line instead of
156157
a "repository:" line, then the argument to "directory:" is the name
157158
of a directory that contains multiple repository files, each ending
158159
with ".fossil". For example:
159160
<blockquote><pre>
@@ -200,44 +201,44 @@
200201
HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING
201202
</pre>
202203
</blockquote>
203204
<h2>Additional CGI Script Options</h2>
204205
<blockquote>
205
-<p>
206
+
206207
The CGI script can have additional options used to fine-tune
207208
Fossil's behavior. See the [./cgi.wiki|CGI script documentation]
208209
for details.
209
-</p>
210
+
210211
</blockquote>
211212
<h2>Additional Observations</h2>
212213
<blockquote><ol type="I">
213214
<li><p>
214215
Fossil does not distinguish between the various HTTP methods (GET, PUT,
215216
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>
217218
<li><p>
218219
Fossil does not distinguish between query parameters that are part of the
219220
URI, application/x-www-form-urlencoded or multipart/form-data encoded
220221
parameter that are part of the POST content, and cookies. Each information
221222
source is seen as a space of key/value pairs which are loaded into an
222223
internal property hash table. The code that runs to generate the reply
223224
can then reference various properties values.
224225
Fossil does not care where the value of each property comes from (POST
225226
content, cookies, or query parameters) only that the property exists
226
-and has a value.
227
+and has a value.</p></li>
227228
<li><p>
228229
The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands
229230
are implemented using a simple built-in web server that accepts incoming HTTP
230231
requests, translates each request into a CGI invocation, then creates a
231232
separate child Fossil process to handle each request. In other words, CGI
232233
is used internally to implement "fossil ui/server".
233
-<p>
234
+<br><br>
234235
SCGI is processed using the same built-in web server, just modified
235236
to parse SCGI requests instead of HTTP requests. Each SCGI request is
236237
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>
238239
<li><p>
239240
Fossil is itself often launched using CGI. But Fossil can also then
240241
turn around and launch [./serverext.wiki|sub-CGI scripts to implement
241
-extensions].
242
+extensions].</p></li>
242243
</ol>
243244
</blockquote>
244245
--- 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&#91;1&#93;</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&#91;1&#93;</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
--- www/adding_code.wiki
+++ www/adding_code.wiki
@@ -118,11 +118,11 @@
118118
119119
<blockquote><verbatim>
120120
/*
121121
** COMMAND: xyzzy
122122
**
123
-** Help text goes here.
123
+** Help text goes here. Backslashes must be escaped.
124124
*/
125125
void xyzzy_cmd(void){
126126
/* Implement the command here */
127127
fossil_print("Hello, World!\n");
128128
}
129129
--- 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 @@
366366
Statistically, this will happen about once every 70 or so messages, so
367367
it is important to give this option if your MTA treats leading dots on a
368368
line this way.
369369
370370
<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
374373
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.
376377
377378
It is probably also possible to configure [`procmail`][pmdoc] to work
378379
with this protocol. If you know how to do it, a patch to this document
379380
or a how-to on [the Fossil forum][ff] would be appreciated.
380381
381382
--- 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
--- www/blockchain.md
+++ www/blockchain.md
@@ -60,11 +60,11 @@
6060
* **Type 2** is creation of new fraudulent currency that will pass
6161
in commerce. To extend our analogy, it is the creation of new
6262
US $10 bills. There are two sub-types to this fraud. In terms of
6363
our analogy, they are:
6464
65
- * **Type 2a**: copying an existing legitimate $10 bill<p>
65
+ * **Type 2a**: copying an existing legitimate $10 bill<br><br>
6666
6767
* **Type 2b**: printing a new $10 bill that is unlike an existing
6868
legitimate one, yet which will still pass in commerce
6969
7070
* **Type 3** is double-spending existing legitimate cryptocurrency.
7171
--- 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 @@
11
<title>Compiling and Installing Fossil</title>
22
33
<h2>0.0 Using A Pre-compiled Binary</h2>
44
5
-<p>[/uv/download.html|Pre-compiled binaries] are available for recent
5
+[/uv/download.html|Pre-compiled binaries] are available for recent
66
releases. Just download
77
the appropriate executable for your platform
88
and put it on your $PATH.
99
To uninstall, simply delete the executable.
1010
To upgrade from an older release, just overwrite the older binary with
11
-the newer one.</p>
11
+the newer one.
1212
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].
1515
1616
1717
<h2>0.1 Executive Summary</h2>
1818
19
-<p>Building and installing is very simple. Three steps:</p>
19
+Building and installing is very simple. Three steps:
2020
2121
<ol>
2222
<li> Download and unpack a source tarball or ZIP.
2323
<li> <b>./configure; make</b>
2424
<li> Move the resulting "fossil" or "fossil.exe" executable to someplace on
2525
your $PATH.
2626
</ol>
2727
28
-<p><hr>
28
+<hr>
2929
3030
<h2>1.0 Obtaining The Source Code</h2>
3131
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
3333
containing a snapshot of the <em>latest</em> version directly from
3434
Fossil's own fossil repository. Additionally, source archives of
3535
<em>released</em> versions of
3636
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:
3838
3939
<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>
4141
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>
4444
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
4646
version on the trunk branch is usually a good choice. Click on its
47
-link.</p></li>
47
+link.</li>
4848
49
-<li><p>Finally, click on one of the
49
+<li>Finally, click on one of the
5050
"Zip Archive" or "Tarball" links, according to your preference.
5151
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>
5353
</ol>
5454
5555
<h2>Aside: Is it really safe to use an unreleased development version of
5656
the Fossil source code?</h2>
5757
@@ -74,78 +74,82 @@
7474
7575
<h2>2.0 Compiling</h2>
7676
7777
<ol>
7878
<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>
8181
8282
<li><i>(Optional, Debian-compatible Linux only)</i>
8383
Make sure you have all the necessary tools and libraries at hand by running:
8484
<b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>.
8585
8686
<li><i>(Optional, Unix only)</i>
8787
Run <b>./configure</b> to construct a makefile.
8888
8989
<ol type="a">
90
-<li><p>
90
+<li>
9191
The build system for Fossil on Unix-like systems assumes that the
9292
OpenSSL development and runtime files are available on your system,
9393
because unprotected repositories are trivial to attack otherwise.
9494
Indeed, some public Fossil repositories — including Fossil's own — today
9595
run in an HTTPS-only mode, so that you can't even do an anonymous clone
9696
from them without using the TLS features added to Fossil by OpenSSL. To
9797
weaken that stance could allow a
9898
[https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the
9999
middle attack], such as one that substitutes malicious code into your
100
-Fossil repository clone.</p>
100
+Fossil repository clone.
101101
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
103103
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.
105105
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,
107107
we recommend that you install them, typically via your OS's package
108108
manager. The Fossil build system goes to a lot of effort to seek these
109109
out wherever they may be found, so that is typically all you need to
110
-do.</p>
110
+do.
111111
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>
114115
115
-<li><p>
116
+<li>
116117
To build a statically linked binary, you can <i>try</i> adding
117118
the <b>--static</b> option, but
118119
[https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
119120
| it may well not work]. If your platform of choice is affected by this,
120121
the simplest workaround we're aware of is to build a Fossil container,
121122
then [./containers.md#static | extract the static executable from it].
123
+</li>
122124
123
-<li><p>
125
+<li>
124126
To enable the native [./th1.md#tclEval | Tcl integration feature] feature,
125127
add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options.
128
+</li>
126129
127
-<li><p>
130
+<li>
128131
Other configuration options can be seen by running
129132
<b>./configure --help</b>
133
+</li>
130134
</ol>
131135
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>
134138
135139
<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>
138142
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
140144
running configure, you can also use: <b>make -f Makefile.classic</b>. You may
141145
want to make minor edits to Makefile.classic to configure the build for your
142
-system.
146
+system.</li>
143147
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
145149
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>
147151
148152
There is limited support for building under MinGW's native Windows port
149153
of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but
150154
it's better to build under MSYS, Cygwin, or WSL on Windows since this
151155
mode doesn't take care of cases such as the "openssl" target, which
@@ -170,11 +174,11 @@
170174
Alternatively, running <b>./configure</b> under MSYS should give a
171175
suitable top-level Makefile. However, options passed to configure that are
172176
not applicable on Windows may cause the configuration or compilation to fail
173177
(e.g. fusefs, internal-sqlite, etc).
174178
175
-<li><p><i>MSVC</i> → Use the MSVC makefile.
179
+<li><i>MSVC</i> → Use the MSVC makefile.</li>
176180
177181
Run all of the following from a "x64 Native Tools Command Prompt".
178182
179183
First
180184
change to the "win/" subdirectory ("<b>cd win</b>") then run
@@ -202,58 +206,63 @@
202206
</pre></blockquote>
203207
<blockquote><pre>
204208
buildmsvc.bat FOSSIL_ENABLE_TCL=1
205209
</pre></blockquote>
206210
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
208212
recommended to configure using: "<b>configure --disable-internal-sqlite</b>",
209213
making sure you have the "libsqlite3-devel" , "zlib-devel" and
210
-"openssl-devel" packages installed first.
214
+"openssl-devel" packages installed first.</li>
211215
</ol>
212216
</ol>
213217
214218
<h2>3.0 Installing</h2>
215219
216220
<ol>
217221
<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).
219223
Put this binary in a
220224
directory that is somewhere on your PATH environment variable.
221
-It does not matter where.</p>
225
+It does not matter where.
226
+</li>
222227
223228
<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>
226232
</ol>
227233
228234
<h2>4.0 Additional Considerations</h2>
229235
230236
<ul>
231
-<li><p>
237
+<li>
232238
If the makefiles that come with Fossil do not work for
233239
you, or for some other reason you want to know how to build
234240
Fossil manually, then refer to the
235241
[./makefile.wiki | Fossil Build Process] document which describes
236242
in detail what the makefiles do behind the scenes.
243
+</li>
237244
238
-<li><p>
245
+<li>
239246
The fossil executable is self-contained and stand-alone and usually
240247
requires no special libraries or other software to be installed. However,
241248
the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk
242249
be installed on the local machine. You can get Tcl/Tk from
243250
[http://www.activestate.com/activetcl|ActiveState].
251
+</li>
244252
245
-<li><p>
253
+<li>
246254
To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile
247255
generated by configure to add the following lines:
248256
<blockquote><pre>
249257
TCC += -DSQLITE_WITHOUT_ZONEMALLOC
250258
TCC += -D_BSD_SOURCE
251259
TCC += -DWITHOUT_ICONV
252260
TCC += -Dsocketlen_t=int
253261
TCC += -DSQLITE_MAX_MMAP_SIZE=0
254
-</pre></blockquote>
262
+ </pre></blockquote>
263
+</li>
255264
</ul>
256265
257266
258267
<h2 id="docker" name="oci">5.0 Building a Docker Container</h2>
259268
260269
--- 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 @@
11
<title>Change Log</title>
22
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.
421
* The stock OCI container no longer includes BusyBox, thus no longer
522
needs to start as root to chroot that power away. That in turn
623
frees us from needing to build and install the container as root,
724
since it no longer has to create a private <tt>/dev</tt> tree
825
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>
944
1045
<h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
1146
* Users can request a password reset. This feature is disabledby default. Use
1247
the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it.
1348
New web pages [/help?cmd=/resetpw|/resetpw] and
@@ -64,17 +99,17 @@
6499
numeric order even if they contain a different number of digits.
65100
(Example: "fossil_80_..." comes before "fossil_100.png" in the
66101
[/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.)
67102
* Enhancements to the graph layout algorithm design to improve readability
68103
and promote better situational awareness.
69
- * Performance enhancement for the
104
+ * Performance enhancement for the
70105
[./checkin_names.wiki#root|"root:BRANCHNAME" style of tag],
71106
accomplished using a Common Table Expression in the underlying SQL.
72107
* Sort tag listings (command line and webpage) by taking numbers into
73108
consideration so as to cater for tags that follow semantic versioning.
74109
* On the wiki listings, omit by default wiki pages that are associated with
75
- check-ins and branches.
110
+ check-ins and branches.
76111
* Add the new "[/help?cmd=describe|fossil describe]" command.
77112
* Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
78113
See corresponding [../test/markdown-test3.md|test cases],
79114
[/wiki?name=branch/markdown-footnotes#il|known limitations] and
80115
[forum:/forumthread/ee1f1597e46ec07a|discussion].
@@ -123,11 +158,11 @@
123158
</ul>
124159
* Promote the test-detach command into the [/help?cmd=detach|detach command].
125160
* For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option,
126161
if no URL is specified then use the last URL from the most recent prior
127162
"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
129164
"[/help?cmd=init|fossil init]" command.
130165
* The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment
131166
variable for clients.
132167
* Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
133168
that it includes the query string. This is how most other systems understand
@@ -145,20 +180,20 @@
145180
<li> Performance improvements
146181
</ul>
147182
* The --branchcolor option on [/help?cmd=commit|fossil commit] and
148183
[/help?cmd=amend|fossil amend] can now take the value "auto" to
149184
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
151186
[/help?cmd=open|fossil open].
152187
* Add the [/help?cmd=ticket-default-report|ticket-default-report setting],
153188
which if set to the title of a ticket report causes that ticket report
154189
to be displayed below the search box in the /ticket page.
155190
* The "nc" query parameter to the [/help?cmd=/timeline|/timeline] page
156191
causes all graph coloring to be omitted.
157192
* Improvements and bug fixes to the new "[/help?cmd=ui|fossil ui REMOTE]"
158193
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
160195
the current page and list URLs suitable for pasting them into the page.
161196
* Add the --no-http-compression option to [/help?cmd=sync|fossil sync]
162197
and similar.
163198
* Print total payload bytes on a [/help?cmd=sync|fossil sync] when using
164199
the --verbose option.
@@ -249,11 +284,11 @@
249284
* The [./defcsp.md|default CSP] has been relaxed slightly to allow
250285
images to be loaded from any URL. All other resources are still
251286
locked down by default.
252287
* The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
253288
setting to determine the content of the main menu.
254
- The ability to edit the
289
+ The ability to edit the
255290
"mainmenu" setting is added on the /Admin/Configuration page.
256291
* The hamburger menu is now available on most of the built-in skins.
257292
* Any built-in skin named "X" can be used instead of the standard
258293
repository skin by adding the URL parameter <tt>skin=X</tt> to the
259294
request. The selection is persisted using the display
@@ -264,43 +299,43 @@
264299
/sitemap, so that it appears in hamburger menus.
265300
* The [/sitemap] extensions are now specified by a single new
266301
"[/help?cmd=sitemap-extra|sitemap-extra setting]",
267302
rather than a cluster of various
268303
"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
270305
adjustments on servers that use /sitemap extensions.</b>
271306
The /Admin/Configuration page provides the ability to edit
272307
the new "sitemap-extra" setting.
273
- * Added the "--ckout-alias NAME" option to
308
+ * Added the "--ckout-alias NAME" option to
274309
[/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and
275310
[/help?cmd=http|fossil http]. This option causes Fossil to
276311
understand URIs of the form "/doc/NAME/..." as if they were
277312
"[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
278313
[./embeddeddoc.wiki|embedded documentation] changes prior to
279314
check-in.
280315
* 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
282317
"[/help?cmd=preferred-diff-type|preferred-diff-type]"
283318
setting is omitted or less than 1, then select the diff type based
284319
on a guess of whether or not the request is coming from a mobile
285320
device. Mobile gets unified and desktop gets side-by-side.
286321
* The various pages which show diffs now have toggles to show/hide
287322
individual diffs.
288323
* Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
289324
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
291326
"detail.txt" skin files, for better control of Pikchr
292327
colors in inverted color schemes.
293
- * Add the <tt>--list</tt> option to the
328
+ * Add the <tt>--list</tt> option to the
294329
[/help?cmd=tarball|tarball],
295330
[/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
296331
commands.
297332
* The javascript used to implement the hamburger menu on the
298333
default built-in skin has been made generic so that it is usable
299334
by a variety of skins, and promoted to an ordinary built-in
300335
javascript file.
301
- * New TH1 commands:
336
+ * New TH1 commands:
302337
"[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
303338
"[/doc/trunk/www/th1.md#capexpr|capexpr]",
304339
"foreach", "lappend", and "string match"
305340
* The [/help/leaves|leaves command] now shows the branch point
306341
of each leaf.
@@ -333,11 +368,11 @@
333368
* <b>Schema Update Notice #1:</b>
334369
This release drops a trigger from the database schema (replacing
335370
it with a TEMP trigger that is created as needed). This
336371
change happens automatically the first time you
337372
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
339374
version 2.14 and then later downgrade or otherwise use an earlier
340375
version of Fossil, the email notification mechanism may fail
341376
to send out notifications for some events, due to the missing
342377
trigger. If you want to
343378
permanently downgrade an installation, then you should run
@@ -358,11 +393,11 @@
358393
from the remote URL and the newly cloned repo is opened. This makes
359394
the clone command work more like Git, thus making it easier for
360395
people transitioning from Git.
361396
* Added the --mainbranch option to the [/help?cmd=git|fossil git export]
362397
command.
363
- * Added the --format option to the
398
+ * Added the --format option to the
364399
"[/help?cmd=timeline|fossil timeline]" command.
365400
* Enhance the --numstat option on the
366401
"[/help?cmd=diff|fossil diff]" command so that it shows a total
367402
number of lines added and deleted and total number of files
368403
modified.
@@ -480,11 +515,11 @@
480515
of the checkin that occured on 2020-06-27 15:06 going back to
481516
the 2.11 release.
482517
* Update the built-in SQLite so that the
483518
"[/help?cmd=sql|fossil sql]" command supports new output
484519
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
486521
"[/help?cmd=sql|fossil sql]" command.
487522
* Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
488523
the "[/help?cmd=sql|fossil sql]" command, providing access to the
489524
dispatch table including all help text, and the builtin data files,
490525
respectively.
@@ -517,11 +552,11 @@
517552
to work without the rebuild, but the new backlinks will be missing.</ul>
518553
* The algorithm for finding the
519554
[./tech_overview.wiki#configloc|location of the configuration database]
520555
is enhanced to be XDG-compliant.
521556
* Add a hide/show feature to
522
- [./wikitheory.wiki#assocwiki|associated wiki] display on
557
+ [./wikitheory.wiki#assocwiki|associated wiki] display on
523558
check-in and branch information pages.
524559
* Enhance the "[/help?cmd=info|fossil info]" command so that it
525560
works with no arguments even if not within an open check-out.
526561
* Many improvements to the forum and especially email notification
527562
of forum posts, in response to community feedback after switching
528563
--- 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 @@
159159
of the manifest is the identifier for the entire check-in. When
160160
you look at a "timeline" of changes in Fossil, the ID associated
161161
with each check-in or commit is really just the artifact ID of the
162162
manifest for that check-in.
163163
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,
165165
the manifest is computed in memory by Fossil whenever it needs it.
166166
However, the "fossil setting manifest on" command will cause the
167167
manifest file to be materialized to disk, if desired. Both Fossil
168168
itself, and SQLite cause the manifest file to be materialized to disk
169169
so that the makefiles for these project can read the manifest and
170170
embed version information in generated binaries.
171171
172
-<p>Fossil automatically generates a manifest whenever you "commit"
172
+Fossil automatically generates a manifest whenever you "commit"
173173
a new check-in. So this is not something that you, the developer,
174174
need to worry with. The format of a manifest is intentionally
175175
designed to be simple to parse, so that if
176176
you want to read and interpret a manifest, either by hand or
177177
with a script, that is easy to do. But you will probably never
178
-need to do so.</p>
178
+need to do so.
179179
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
181181
manifest also contains a check-in comment, the date and time
182182
when the check-in was established, who created the check-in,
183183
and links to other check-ins from which the current check-in
184184
is derived. There is also a couple of checksums used to verify
185185
the integrity of the check-in. And the whole manifest might
186
-be PGP clearsigned.</p>
186
+be PGP clearsigned.
187187
188188
<h3 id="keyconc">2.3 Key concepts</h3>
189189
190190
<ul>
191191
<li>A <b>check-in</b> is a set of files arranged
@@ -435,29 +435,27 @@
435435
<h2>5.0 Setting Up A Fossil Server</h2>
436436
437437
With other configuration management software, setting up a server is
438438
a lot of work and normally takes time, patience, and a lot of system
439439
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.
459457
460458
See the [./server/ | How To Configure A Fossil Server] document
461459
for details.
462460
463461
<h2>6.0 Review Of Key Concepts</h2>
464462
--- 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 @@
6969
The wrong way is to use the `Dockerfile COPY` command, because by baking
7070
the repo into the image at build time, it will become one of the image’s
7171
base layers. The end result is that each time you build a container from
7272
that image, the repo will be reset to its build-time state. Worse,
7373
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
7575
want.
7676
7777
The correct ways put the repo into the _container_ created from the
7878
_image_, not in the image itself.
7979
@@ -114,11 +114,11 @@
114114
this with `chmod`: simply reload the browser, and Fossil will try again.
115115
116116
117117
### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container
118118
119
-The simple storage method above has a problem: Docker containers are
119
+The simple storage method above has a problem: containers are
120120
designed to be killed off at the slightest cause, rebuilt, and
121121
redeployed. If you do that with the repo inside the container, it gets
122122
destroyed, too. The solution is to replace the “run” command above with
123123
the following:
124124
@@ -133,12 +133,12 @@
133133
Because this bind mount maps a host-side directory (`~/museum`) into the
134134
container, you don’t need to `docker cp` the repo into the container at
135135
all. It still expects to find the repository as `repo.fossil` under that
136136
directory, but now both the host and the container can see that repo DB.
137137
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
140140
_would_ need to `docker cp` the repo file into the container.
141141
142142
Either way, files in these mounted directories have a lifetime
143143
independent of the container(s) they’re mounted into. When you need to
144144
rebuild the container or its underlying image — such as to upgrade to a
@@ -183,152 +183,25 @@
183183
184184
## 3. <a id="security"></a>Security
185185
186186
### 3.1 <a id="chroot"></a>Why Not Chroot?
187187
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
330203
331204
The example commands above create the container with [a default set of
332205
Linux kernel capabilities][defcap]. Although Docker strips away almost
333206
all of the traditional root capabilities by default, and Fossil doesn’t
334207
need any of those it does take away, Docker does leave some enabled that
@@ -443,12 +316,12 @@
443316
444317
445318
## 4. <a id="static"></a>Extracting a Static Binary
446319
447320
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.
450323
451324
A secondary benefit falls out of this process for free: it’s arguably
452325
the easiest way to build a purely static Fossil binary for Linux. Most
453326
modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
454327
back-to-basics nature makes static builds work the way they used to,
@@ -460,17 +333,17 @@
460333
$ docker create --name fossil-static-tmp fossil
461334
$ docker cp fossil-static-tmp:/bin/fossil .
462335
$ docker container rm fossil-static-tmp
463336
```
464337
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.
467340
468341
[lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
469342
470343
471
-## 5. <a id="args"></a>Container Build Arguments
344
+## 5. <a id="custom" name="args"></a>Customization Points
472345
473346
### <a id="pkg-vers"></a> 5.1 Fossil Version
474347
475348
The default version of Fossil fetched in the build is the version in the
476349
checkout directory at the time you run it. You could override it to get
@@ -486,21 +359,21 @@
486359
$ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
487360
```
488361
489362
While you could instead use the generic
490363
“`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
492365
reuse them across builds. If you ask for “`release`” before a new
493366
version is tagged and then immediately after, you might expect to get
494367
two different tarballs, but because the underlying source tarball URL
495368
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
497370
even if you pass the “`docker build --no-cache`” option.
498371
499372
This is why we default to pulling the Fossil tarball by checkin ID
500373
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
502375
pull a fresh tarball.
503376
504377
505378
### 5.2 <a id="uids"></a>User & Group IDs
506379
@@ -512,22 +385,34 @@
512385
close to zero as we can manage.
513386
514387
To change it to something else, say:
515388
516389
```
517
- $ make container-image \
518
- DBFLAGS='--build-arg UID=501'
390
+ $ make container-image DBFLAGS='--build-arg UID=501'
519391
```
520392
521393
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
523395
file permissions. You may therefore wish them to mean something on both
524396
sides of the container barrier rather than have “499” appear on the host
525397
in “`ls -l`” output.
526398
527399
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
529414
530415
You can use this same mechanism to enable non-default Fossil
531416
configuration options in your build. For instance, to turn on
532417
the JSON API and the TH1 docs extension:
533418
@@ -535,31 +420,133 @@
535420
$ make container-image \
536421
DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"'
537422
```
538423
539424
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
561548
562549
563550
## 6. <a id="light"></a>Lightweight Alternatives to Docker
564551
565552
Those afflicted with sticker shock at seeing the size of a [Docker
@@ -567,13 +554,13 @@
567554
“noped” out of the whole concept of containers. The first thing to
568555
realize is that when it comes to actually serving simple containers like
569556
the ones shown above is that [Docker Engine][DE] suffices, at about a
570557
quarter of the size.
571558
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
575562
blame you if you noped right on out of here, but if you will be patient,
576563
you will find that there are ways to run Fossil inside a container even
577564
on entry-level cloud VPSes. These are well-suited to running Fossil; you
578565
don’t have to resort to [raw Fossil service][srv] to succeed,
579566
leaving the benefits of containerization to those with bigger budgets.
@@ -587,11 +574,11 @@
587574
--publish 127.0.0.1:9999:8080
588575
```
589576
590577
The assumption is that there’s a reverse proxy running somewhere that
591578
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
593580
publishing effectively replaces the use of the
594581
“`fossil server --localhost`” option.
595582
596583
For the nginx case, you need to add `--scgi` to these commands, and you
597584
might also need to specify `--baseurl`.
@@ -616,17 +603,18 @@
616603
617604
618605
### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down
619606
620607
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
622609
[`nerdctl`][nerdctl] and you have enough of the engine to run Fossil
623610
containers. The big things you’re missing are:
624611
625612
* **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.
628616
629617
* **SwarmKit**: A powerful yet simple orchestrator for Docker that you
630618
probably aren’t using with Fossil anyway.
631619
632620
In exchange, you get a runtime that’s about half the size of Docker
@@ -640,12 +628,12 @@
640628
[runc]: https://github.com/opencontainers/runc
641629
642630
643631
### 6.2 <a id="podman"></a>Podman
644632
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
647635
Red Hat and thus popular on that family of OSes, it will run on
648636
any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac]
649637
or [on Windows via WSL2][pmwin].
650638
651639
On Ubuntu 22.04, the installation size is about 38&nbsp;MiB, roughly a
@@ -680,15 +668,15 @@
680668
--publish 127.0.0.1:9999:8080 \
681669
localhost/fossil
682670
$ podman start fossil
683671
```
684672
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
690678
691679
692680
### 6.3 <a id="nspawn"></a>`systemd-container`
693681
694682
If even the Podman stack is too big for you, the next-best option I’m
@@ -788,11 +776,11 @@
788776
* The path in the host-side part of the `Bind` value must point at the
789777
directory containing the `repo.fossil` file referenced in said
790778
command so that `/museum/repo.fossil` refers to your repo out
791779
on the host for the reasons given [above](#bind-mount).
792780
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
794782
`/etc/systemd/system/[email protected]`, containing:
795783
796784
----
797785
798786
```
@@ -841,11 +829,11 @@
841829
```
842830
843831
You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
844832
to allow Fossil to bind to this low-numbered port.
845833
846
-We use systemd’s template file feature to allow multiple Fossil
834
+We use the `systemd` template file feature to allow multiple Fossil
847835
servers running on a single machine, each on a different TCP port,
848836
as when proxying them out as subdirectories of a larger site.
849837
To add another project, you must first clone the base “machine” layer:
850838
851839
```
@@ -996,11 +984,11 @@
996984
you’re serving Fossil from, you may need to know which assumptions
997985
our container violates and the resulting consequences.
998986
999987
Some of it we discussed above already, but there’s one big class of
1000988
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
1002990
rather than “boot” an OS image. That causes a bunch of commands to fail:
1003991
1004992
* **`machinectl poweroff`** will fail because the container
1005993
isn’t running dbus.
1006994
1007995
--- 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&nbsp;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&nbsp;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
--- www/custom_ticket.wiki
+++ www/custom_ticket.wiki
@@ -1,16 +1,16 @@
11
<title>Customizing The Ticket System</title>
22
<nowiki>
33
<h2>Introduction</h2>
4
-<p>
4
+
55
This guide will explain how to add the "assigned_to" and "submitted_by" fields
66
to the ticket system in Fossil, as well as making the system more useful. You
77
must have "admin" access to the repository to implement these instructions.
8
-</p>
8
+
9
+<h2>First modify the TICKET table</h2>
910
10
-<h2>First modify the TICKET table</h2><blockquote>
11
-<p>
11
+<blockquote>
1212
Click on the "Admin" menu, then "Tickets", then "Table". After the other fields
1313
and before the final ")", insert:
1414
<pre>
1515
,
1616
assigned_to TEXT,
@@ -17,15 +17,15 @@
1717
opened_by TEXT
1818
</pre>
1919
And "Apply Changes". You have just added two more fields to the ticket
2020
database! NOTE: I won't tell you to "Apply Changes" after each step from here
2121
on out. Now, how do you use these fields?
22
-</p>
2322
</blockquote>
2423
25
-<h2>Next add assignees</h2><blockquote>
26
-<p>
24
+<h2>Next add assignees</h2>
25
+
26
+<blockquote>
2727
Back to the "Tickets" admin page, and click "Common". Add something like this:
2828
<pre>
2929
set assigned_choices {
3030
unassigned
3131
tom
@@ -34,15 +34,15 @@
3434
}
3535
</pre>
3636
Obviously, choose names corresponding to the logins on your system. The
3737
'unassigned' entry is important, as it prevents you from having a NULL in that
3838
field (which causes problems later when editing).
39
-</p>
4039
</blockquote>
4140
42
-<h2>Now modify the 'new ticket' page</h2><blockquote>
43
-<p>
41
+<h2>Now modify the 'new ticket' page</h2>
42
+
43
+<blockquote>
4444
Back to the "Tickets" admin page, and click "New Ticket Page". This is a little
4545
more tricky. Edit the top part:
4646
<pre>
4747
if {[info exists submit]} {
4848
set status Open
@@ -66,22 +66,21 @@
6666
&lt;th1>enable_output 1&lt;/th1>
6767
</pre>
6868
This bit of code will get rid of the "email" field entry for logged-in users.
6969
Since we know the user's information, we don't have to ask for it. NOTE: it
7070
might be good to automatically scoop up the user's email and put it here.
71
-</p>
72
-<p>
71
+
7372
You might also want to enable people to actually assign the ticket to a specific
7473
person during creation. For this to work, you need to add the code
7574
for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page".
7675
This will give you an additional combobox where you can choose a person during
7776
ticket creation.
78
-</p>
7977
</blockquote>
8078
81
-<h2>Modify the 'view ticket' page</h2><blockquote>
82
-<p>
79
+<h2>Modify the 'view ticket' page</h2>
80
+
81
+<blockquote>
8382
Look for the text "Contact:" (about halfway through). Then insert these lines
8483
after the closing tr tag and before the "enable_output" line:
8584
<pre>
8685
<tr>
8786
&lt;td align="right">Assigned to:&lt;/td>&lt;td bgcolor="#d0d0d0">
@@ -91,15 +90,15 @@
9190
$&lt;opened_by>
9291
&lt;/td>
9392
</pre>
9493
This will add a row which displays these two fields, in the event the user has
9594
<a href="./caps/ref.html#w">ticket "edit" capability</a>.
96
-</p>
9795
</blockquote>
9896
99
-<h2>Modify the 'edit ticket' page</h2><blockquote>
100
-<p>
97
+<h2>Modify the 'edit ticket' page</h2>
98
+
99
+<blockquote>
101100
Before the "Severity:" line, add this:
102101
<pre>
103102
&lt;tr>&lt;td align="right">Assigned to:&lt;/td>&lt;td>
104103
&lt;th1>combobox assigned_to $assigned_choices 1&lt;/th1>
105104
&lt;/td>&lt;/tr>
@@ -107,25 +106,25 @@
107106
That will give you a drop-down list of assignees. The first argument to the TH1
108107
command 'combobox' is the database field which the combobox is associated to.
109108
The next argument is the list of choices you want to show in the combobox (and
110109
that you specified in the second step above. The last argument should be 1 for a
111110
true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for
112
-details).</p>
111
+details).
113112
114
-<p>Now, similar to the previous
113
+Now, similar to the previous
115114
section, look for "Contact:" and add this:
116115
<pre>
117116
&lt;tr>&lt;td align="right">Reported by:&lt;/td>&lt;td>
118117
&lt;input type="text" name="opened_by" size="40"
119118
value="$&lt;opened_by>">
120119
&lt;/td>&lt;/tr>
121120
</pre>
122
-</p>
123121
</blockquote>
124122
125
-<h2>What next?</h2><blockquote>
126
-<p>
123
+<h2>What next?</h2>
124
+
125
+<blockquote>
127126
Now you can add custom reports which select based on the person to whom the
128127
ticket is assigned. For example, an "Assigned to me" report could be:
129128
<pre>
130129
SELECT
131130
CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc'
@@ -141,8 +140,7 @@
141140
subsystem,
142141
title
143142
FROM ticket
144143
WHERE assigned_to=user()
145144
</pre>
146
-</p>
147145
</blockquote>
148146
</nowiki>
149147
--- 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 &lt;th1>enable_output 1&lt;/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 &lt;td align="right">Assigned to:&lt;/td>&lt;td bgcolor="#d0d0d0">
@@ -91,15 +90,15 @@
91 $&lt;opened_by>
92 &lt;/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 &lt;tr>&lt;td align="right">Assigned to:&lt;/td>&lt;td>
104 &lt;th1>combobox assigned_to $assigned_choices 1&lt;/th1>
105 &lt;/td>&lt;/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 &lt;tr>&lt;td align="right">Reported by:&lt;/td>&lt;td>
118 &lt;input type="text" name="opened_by" size="40"
119 value="$&lt;opened_by>">
120 &lt;/td>&lt;/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 &lt;th1>enable_output 1&lt;/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 &lt;td align="right">Assigned to:&lt;/td>&lt;td bgcolor="#d0d0d0">
@@ -91,15 +90,15 @@
90 $&lt;opened_by>
91 &lt;/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 &lt;tr>&lt;td align="right">Assigned to:&lt;/td>&lt;td>
103 &lt;th1>combobox assigned_to $assigned_choices 1&lt;/th1>
104 &lt;/td>&lt;/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 &lt;tr>&lt;td align="right">Reported by:&lt;/td>&lt;td>
117 &lt;input type="text" name="opened_by" size="40"
118 value="$&lt;opened_by>">
119 &lt;/td>&lt;/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 @@
7878
generates the initial HTML Header section of a page. The
7979
generated HTML Header will look something like this:
8080
8181
<html>
8282
<head>
83
- <base href="..." />
84
- <meta http-equiv="Content-Security-Policy" content="...." />
83
+ <base href="...">
84
+ <meta http-equiv="Content-Security-Policy" content="....">
8585
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8686
<title>....</title>
87
- <link rel="stylesheet" href="..." type="text/css" />
87
+ <link rel="stylesheet" href="..." type="text/css">
8888
</head>
8989
<body class="FEATURE">
9090
9191
…where `FEATURE` is either the top-level URL element (e.g. `doc`) or a
9292
feature class that groups multiple URLs under a single name such as
@@ -180,21 +180,21 @@
180180
desired result.
181181
182182
The skin is controlled by five files:
183183
184184
<blockquote><dl>
185
-<dt><b>css.txt</b></dt><dd>
185
+<dt><b>css.txt</b></dt>
186186
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.
188188
Fossil might add additional CSS elements after
189189
the css.txt file, if it sees that the css.txt omits some
190190
CSS components that Fossil needs. But for the most part,
191191
the content of the css.txt is the CSS for the page.</dd>
192192
193
-<dt><b>details.txt</b><dt><dd>
193
+<dt><b>details.txt</b><dt>
194194
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
196196
the look and feel, mostly of the timeline. The default
197197
details.txt file looks like this:
198198
199199
<blockquote><pre>
200200
pikchr-background: ""
@@ -211,11 +211,11 @@
211211
of certain aspects of the timeline graph. The number on the
212212
right is a boolean - "1" to activate the feature and "0" to
213213
disable it. The "white-foreground:" setting should be set to
214214
"1" if the page color has light-color text on a darker background,
215215
and "0" if the page has dark text on a light-colored background.
216
-<p>
216
+
217217
If the "pikchr-foreground" setting (added in Fossil 2.14)
218218
is defined and is not an empty string then it specifies a
219219
foreground color to use for [pikchr diagrams](./pikchr.md). The
220220
default pikchr foreground color is black, or white if the
221221
"white-foreground" boolean is set. The "pikchr-background"
@@ -224,24 +224,24 @@
224224
empty strings, then they should be floating point values (close
225225
to 1.0) that specify relative scaling of the fonts in pikchr
226226
diagrams and other elements of the diagrams, respectively.
227227
</dd>
228228
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>
230230
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
232232
and Content Header respectively. Of these, the Content Header is
233233
the most important, as it contains the markup used to generate
234234
the banner and menu bar for each page.
235235
236
-<p>Both the footer.txt and header.txt file are
236
+Both the footer.txt and header.txt file are
237237
[processed using TH1](#headfoot) prior to being output as
238238
part of the overall web page.</dd>
239239
240
-<dt><b>js.txt</b></dt><dd>
240
+<dt><b>js.txt</b></dt>
241241
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.
243243
The complete text of this javascript might be inserted into
244244
the Content Footer, after being processed using TH1, using
245245
code like the following in the "footer.txt" file:
246246
247247
<blockquote><pre>
@@ -248,11 +248,11 @@
248248
&lt;script nonce="$nonce"&gt;
249249
&lt;th1&gt;styleScript&lt;/th1&gt;
250250
&lt;/script&gt;
251251
</pre></blockquote>
252252
253
-<p>The js.txt file was originally used to insert javascript
253
+The js.txt file was originally used to insert javascript
254254
that controls the hamburger menu in the default skin. More
255255
recently, the javascript for the hamburger menu was moved into
256256
a separate built-in file. Skins that use the hamburger menu
257257
typically cause the javascript to be loaded by including the
258258
following TH1 code in the "header.txt" file:
259259
--- 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 &lt;script nonce="$nonce"&gt;
249 &lt;th1&gt;styleScript&lt;/th1&gt;
250 &lt;/script&gt;
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 &lt;script nonce="$nonce"&gt;
249 &lt;th1&gt;styleScript&lt;/th1&gt;
250 &lt;/script&gt;
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
--- www/delta_encoder_algorithm.wiki
+++ www/delta_encoder_algorithm.wiki
@@ -83,11 +83,11 @@
8383
<li>A hash table is filled, mapping from the hashes of the chunks to
8484
the list of chunk locations having this hash.
8585
</li>
8686
</ol>
8787
88
-<h3 id="processing">2.1 Processing the target</h3>
88
+<h3 id="processing">2.2 Processing the target</h3>
8989
9090
<p>This, the main phase of the encoder, processes the target in a loop
9191
from beginning to end. The state of the encoder is captured by two
9292
locations, the "base" and the "slide". "base" points to the first byte
9393
of the target for which no delta output has been generated yet, and
9494
--- 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
--- www/delta_format.wiki
+++ www/delta_format.wiki
@@ -1,34 +1,33 @@
11
<title>Fossil Delta Format</title>
22
<h1>1.0 Overview</h1>
33
4
-<p>Fossil achieves efficient storage and low-bandwidth synchronization
4
+Fossil achieves efficient storage and low-bandwidth synchronization
55
through the use of delta-compression. Instead of storing
66
or transmitting the complete content of an artifact, Fossil stores or
77
transmits only the changes relative to a related artifact.
8
-</p>
98
10
-<p>This document describes the delta-encoding format used by Fossil.
9
+This document describes the delta-encoding format used by Fossil.
1110
The intended audience is developers working on either
1211
<a href="index.wiki">Fossil</a> itself, or on tools compatible with
1312
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.
1514
16
-<p>This document only describes the delta file format. A
15
+This document only describes the delta file format. A
1716
[./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.
1918
2019
<h2>1.1 Sample Software And Analysis Tools</h2>
2120
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
2322
are contained in the [../src/delta.c|delta.c] source file. Interface
2423
logic, including "test-*" commands to directly manipulate deltas are
2524
contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions
2625
to create, apply, and analyze deltas are implemented by code in the
2726
[../src/deltafunc.c|deltafunc.c] source file.
2827
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
3029
deltas and to test the delta logic:
3130
3231
* [/help?cmd=test-delta|fossil test-delta] &rarr; Run self-tests of
3332
the delta logic
3433
@@ -40,11 +39,11 @@
4039
4140
* [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] &rarr; compute
4241
and delta that converts file X into file Y but instead of writing the
4342
delta to output, write performance information about the delta.
4443
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
4645
interactive SQL session connected to the repository, the following
4746
additional SQL functions are provided:
4847
4948
* <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> &rarr;
5049
Compute a data that carries blob X into blob Y and return that delta
@@ -68,53 +67,53 @@
6867
box height 50% "Header"
6968
box same "Segments"
7069
box same "Trailer"
7170
</verbatim>
7271
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.
7574
76
-<p>Both header and trailer provide information about the target
75
+Both header and trailer provide information about the target
7776
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.
7978
8079
<h2 id="header">2.1 Header</h2>
8180
<verbatim type="pikchr">
8281
leftmargin = 0.1
8382
box height 50% "Size"
8483
box same "\"\\n\""
8584
</verbatim>
8685
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
8887
character (ASCII 0x0a). The number is the length of the target in
89
-bytes.</p>
88
+bytes.
9089
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
9291
the target (and allocate any necessary memory based on that) by simply
9392
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.
9594
9695
<h2 id="trailer">2.2 Trailer</h2>
9796
<verbatim type="pikchr">
9897
leftmargin = 0.1
9998
box height 50% "Checksum"
10099
box same "\";\""
101100
</verbatim>
102101
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
104103
0x3b). This number is a checksum of the target and can be used by a
105104
decoder to verify that the delta applied correctly, reconstructing the
106
-target from the original.</p>
105
+target from the original.
107106
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
109108
32-bit integer numbers (MSB first), and summing these up, modulo
110109
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.
112111
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
114113
it available immediately after the target has been reconstructed
115
-fully.</p>
114
+fully.
116115
117116
<h2 id="slist">2.3 Segment-List</h2>
118117
<verbatim type="pikchr">
119118
leftmargin = 0.1
120119
PART1: [
@@ -138,41 +137,41 @@
138137
box "\":\"" same
139138
box "Bytes" same
140139
] with .nw at previous.sw
141140
</verbatim>
142141
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
144143
the original by a combination of inserting literal byte-sequences and
145144
copying ranges of bytes from the original. This is where the
146145
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.
148147
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
150149
generated by each instruction appended after the data of all previous
151
-instructions, with no gaps.</p>
150
+instructions, with no gaps.
152151
153152
<h3 id="insertlit">2.3.1 Insert Literal</h3>
154153
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.
157156
158157
<verbatim type="pikchr">
159158
leftmargin = 0.1
160159
box "Length" height 50%
161160
box "\":\"" same
162161
box "Bytes" same
163162
</verbatim>
164163
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.
167166
168167
<h3 id="copyrange">2.3.2 Copy Range</h3>
169168
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
171170
first byte in the original to copy, and the size of the range, in
172171
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.
174173
175174
<verbatim type="pikchr">
176175
leftmargin = 0.1
177176
box "Length" height 50%
178177
box "\"@\"" same
@@ -179,37 +178,32 @@
179178
box "Offset" same
180179
box "\",\"" same
181180
</verbatim>
182181
183182
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).
186185
187186
<h1 id="intcoding">3.0 Encoding of integers</h1>
188187
189
-<p>
190188
The format currently handles only 32 bit integer numbers. They are
191189
written base-64 encoded, MSB first, and without leading
192190
"0"-characters, except if they are significant (i.e. 0 => "0").
193
-</p>
194191
195
-<p>
196192
The base-64 encoding uses one character for each 6 bits of
197193
the integer to be encoded. The encoding characters are:
198
-</p>
199194
200195
<blockquote><pre>
201196
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
202197
</pre></blockquote>
203198
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
205200
the first character, followed by
206201
the next 6 bits, and so on until all non-zero bits of the integer
207202
are encoded. The minimum number of encoding characters is used.
208203
Note that for integers less than 10, the base-64 coding is a
209204
ASCII decimal rendering of the number itself.
210
-</p>
211205
212206
<h1 id="examples">4.0 Examples</h1>
213207
214208
<h2 id="examplesint">4.1 Integer encoding</h2>
215209
@@ -232,18 +226,18 @@
232226
</tr>
233227
</table>
234228
235229
<h2 id="examplesdelta">4.2 Delta encoding</h2>
236230
237
-<p>An example of a delta using the specified encoding is:</p>
231
+An example of a delta using the specified encoding is:
238232
239233
<table border=1><tr><td><pre>
240234
1Xb
241235
4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre>
242236
</td></tr></table>
243237
244
-<p>This can be taken apart into the following parts:</p>
238
+This can be taken apart into the following parts:
245239
246240
<table border=1>
247241
<tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr>
248242
<tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr>
249243
<tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr>
@@ -258,11 +252,11 @@
258252
<tr><td>&nbsp;</td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr>
259253
<tr><td>&nbsp;</td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr>
260254
<tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr>
261255
</table>
262256
263
-<p>The unified diff behind the above delta is</p>
257
+The unified diff behind the above delta is
264258
265259
<table border=1><tr><td><pre>
266260
bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new
267261
--- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700
268262
+++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700
269263
--- 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] &rarr; 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] &rarr; 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> &rarr;
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>&nbsp;</td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr>
259 <tr><td>&nbsp;</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] &rarr; 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] &rarr; 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> &rarr;
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>&nbsp;</td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr>
253 <tr><td>&nbsp;</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
--- www/encryptedrepos.wiki
+++ www/encryptedrepos.wiki
@@ -5,41 +5,42 @@
55
This technical note explains the process.
66
</blockquote>
77
<h2>Building An Encryption-Enabled Fossil</h2><blockquote>
88
The SQLite Encryption Extension (SEE) is proprietary software and requires
99
[https://sqlite.org/purchase/see|purchasing a license].
10
-<p>
10
+
1111
Assuming you have an SEE license, the first step of compiling Fossil to
1212
use SEE is to create an SEE-enabled version of the SQLite database source code.
1313
This alternative SQLite database source file should be called "sqlite3-see.c"
1414
and should be placed in the extsrc/ subfolder of the Fossil sources, right beside
1515
the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled
1616
"shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder
1717
beside the original "shell.c".
18
-<p>
18
+
1919
Add the --with-see command-line option to the configuration script to enable
2020
the use of SEE on unix-like systems.
2121
<blockquote><pre>
2222
./configure --with-see; make
2323
</pre></blockquote>
24
-<p>To build for Windows using MSVC, add
24
+
25
+To build for Windows using MSVC, add
2526
the "USE_SEE=1" argument to the "nmake" command line.
2627
<blockquote><pre>
2728
nmake -f makefile.msc USE_SEE=1
2829
</pre></blockquote>
2930
</blockquote>
3031
<h2>Using Encrypted Repositories</h2><blockquote>
3132
Any Fossil repositories whose filename ends with ".efossil" is taken to be
3233
an encrypted repository. Fossil will prompt for the encryption password and
3334
attempt to open the repository database using that password.
34
-<p>
35
+
3536
Every invocation of fossil on an encrypted repository requires retyping the
3637
encryption password.
3738
To avoid excess password typing, consider using the "fossil shell"
3839
command which prompts for the password just once, then reuses it for each
3940
subsequent Fossil command entered at the prompt.
40
-<p>
41
+
4142
On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not
4243
(currently) work on an encrypted repository.
4344
</blockquote>
4445
<h2>Additional Security</h2><blockquote>
4546
Use the FOSSIL_SECURITY_LEVEL environment for additional protection.
4647
--- 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 @@
157157
#############################################################################
158158
# Code to actually generate the FAQ
159159
#
160160
puts "<title>Fossil FAQ</title>"
161161
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"
163163
164164
puts {<ol>}
165165
for {set i 1} {$i<$cnt} {incr i} {
166166
puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>"
167167
}
168168
--- 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 @@
11
<title>Fossil FAQ</title>
22
<h1 align="center">Frequently Asked Questions</h1>
33
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>.
55
66
<ol>
77
<li><a href="#q1">What GUIs are available for fossil?</a></li>
88
<li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li>
99
<li><a href="#q3">How do I create a new branch?</a></li>
1010
--- 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
--- www/fiveminutes.wiki
+++ www/fiveminutes.wiki
@@ -4,66 +4,91 @@
44
The following document was contributed by Gilles Ganault on 2013-01-08.
55
</i></b>
66
</p><hr>
77
88
<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
1011
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.
1213
1314
<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.
1619
files, tickets, wiki, etc. It can be located anywhere, although it's considered
1720
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.
1922
2023
<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,
2430
if any, into the current work directory. In addition, it will create a binary
2531
file _FOSSIL_ to keep track of changes (on non-Windows systems it is called
26
-<tt>.fslckout</tt>).</p>
32
+<tt>.fslckout</tt>).
2733
2834
<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 &quot;commit&quot;. When using &quot;.&quot;, 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
3240
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 &quot;*.o,*.obj,*.exe&quot; --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>
3646
3747
<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 &quot;fossil add&quot;.</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>".
4153
4254
<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 &quot;fossil commit&quot;. It's a useful command to run before
46
-running &quot;fossil commit&quot; 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.
4761
4862
<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
5065
for addition, checked-out files that have been edited and must be checked-in,
51
-etc.</p>
66
+etc.
5267
53
-<p>fossil commit -m "Added stuff"</p>
68
+<tt>fossil commit -m "Added stuff"</tt>
5469
5570
If no file names are provided on the command-line then all changes will be checked in,
5671
otherwise just the listed file(s) will be checked in.
5772
5873
<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
+
6689
<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.
7095
--- 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 &quot;commit&quot;. When using &quot;.&quot;, 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 &quot;*.o,*.obj,*.exe&quot; --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 &quot;fossil add&quot;.</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 &quot;fossil commit&quot;. It's a useful command to run before
46 running &quot;fossil commit&quot; 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
--- www/foss-cklist.wiki
+++ www/foss-cklist.wiki
@@ -1,25 +1,25 @@
11
<title>Checklist For Successful Open-Source Projects</title>
22
<nowiki>
33
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
55
blog post <a href="http://spot.livejournal.com/308370.html">
66
http://spot.livejournal.com/308370.html</a> (see also
77
<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>).
88
Tom's original post assigned point scores to the various elements and
99
by adding together the individual points, the reader is supposed to be able
1010
to judge the likelihood that the project will fail.
1111
The point scores, and the items on the list, clearly reflect Tom's
1212
biases and are not necessarily those of the larger open-source community.
1313
Nevertheless, the policy of the Fossil shall be to strive for a perfect
14
-score.</p>
14
+score.
1515
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
1717
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.
1919
20
-<p>See also:
20
+See also:
2121
<ul>
2222
<li><a href="http://offog.org/articles/packaging/">
2323
http://offog.org/articles/packaging/</a>
2424
<li>
2525
<a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases">
@@ -27,36 +27,36 @@
2727
</ul>
2828
2929
<hr>
3030
3131
<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.
3333
34
-<li><p>The project uses a Version Control System (VCS).
34
+<li>The project uses a Version Control System (VCS).
3535
<ol type="a">
3636
<li>The VCS has a working web interface.
3737
<li>There is documentation on how to use the VCS.
3838
<li>The VCS is general-purpose, not something hacked together for this
3939
one project.
4040
</ol>
4141
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
4343
and that documentation is lucid, correct, and up-to-date.
4444
45
-<li><p>The project is configurable using an autoconf-generated configure
45
+<li>The project is configurable using an autoconf-generated configure
4646
script, or the equivalent, and does not require:
4747
<ol type="a">
4848
<li>Manually editing flat files
4949
<li>Editing code header files
5050
</ol>
5151
52
-<li><p>The project should be buildable using commonly available and
52
+<li>The project should be buildable using commonly available and
5353
standard tools like "make".
5454
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.
5656
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
5858
such as zlib and libjpeg.
5959
<ol type="a">
6060
<li>The project does not ship with the sources to standard libraries.
6161
<i>(On the Fossil project, we will allow the SQLite library sources
6262
to be included in the source tree as long as a system-installed
@@ -65,48 +65,48 @@
6565
libraries. Any required bug fixes in standard libraries are pushed
6666
back upstream.
6767
</ol>
6868
6969
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
7171
directory of the installer's choosing.
7272
<ol type="a">
7373
<li>The project contains no unconfigurable hard-coded pathnames like
7474
"/opt" or "/usr/local".
7575
<li>After installation, the source tree can be moved or deleted and
7676
the application will continue working.
7777
</ol>
7878
7979
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.
8181
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.
8383
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
8585
the mailing list.
8686
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.
8890
89
-<li><p>The project has a website.
91
+<li>Release version numbers are in the traditional X.Y or X.Y.Z format.
9092
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
9494
gzip or bzip2 compression.
9595
96
-<li><p>Releases unpack into a versioned top-level directory.
96
+<li>Releases unpack into a versioned top-level directory.
9797
(ex: "projectname-1.2.3/").
9898
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
100100
and the complete text of the license is included in the source code
101101
tarball.
102102
103
-<li><p>There are no incompatible licenses in the code.
103
+<li>There are no incompatible licenses in the code.
104104
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
106106
having gone through the tedious and exacting legal steps to actually put it
107107
in the public domain.
108108
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.
110110
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.
112112
</ol>
113113
--- 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
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -424,82 +424,80 @@
424424
425425
Fossil's normal mode of operation differs on every one of these points,
426426
with the specific designed-in goal of promoting SQLite's cathedral
427427
development model:
428428
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.
501499
502500
Where Git encourages siloed development, Fossil fights against it.
503501
Fossil places a lot of emphasis on synchronizing everyone's work and on
504502
reporting on the state of the project and the work of its developers, so
505503
that everyone — especially the project leader — can maintain a better
506504
--- 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 @@
44
55
Fossil shares many similarities with Git. In many cases, the
66
sub-commands are identical: [`fossil bisect`][fbis] does essentially the
77
same thing as [`git bisect`][gbis], for example.
88
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,
1014
usually because of intentional design differences in Fossil that prevent
1115
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.
1418
1519
We focus on practical command examples here, leaving discussions of the
1620
philosophical underpinnings that drive these command differences to [another
1721
document][fvg]. The [case studies](#cs1) do get a bit philosophical, but
1822
it is with the aim of illustrating how these Fossil design differences
@@ -571,10 +575,40 @@
571575
indeed to any other Fossil command documented to accept a `VERSION` or
572576
`NAME` string.)
573577
574578
[scin]: ./checkin_names.wiki
575579
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
+
576610
577611
<a id="autosync"></a>
578612
## Autosync
579613
580614
Fossil’s [autosync][wflow] feature, normally enabled, has no
@@ -581,26 +615,52 @@
581615
equivalent in Git. If you want Fossil to behave like Git, you can turn
582616
it off:
583617
584618
fossil set autosync 0
585619
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:
595655
596656
1. Your work stays synced up with your coworkers’ efforts as long as your
597657
machine can connect to the remote repository. At need, you can go
598658
off-network and continue work atop the last version you synced with
599659
the remote.
600660
601
-2. It provides immediate off-machine backup of your commits. Unlike
661
+2. You get implicit off-machine backup of your commits. Unlike
602662
centralized version control, though, you can still work while
603663
disconnected; your changes will sync up with the remote once you get
604664
back online.
605665
606666
3. Because there is [little distinction][bu] between the clones in the Fossil
@@ -612,38 +672,106 @@
612672
remote. If the failed remote comes back later, it can sync with the
613673
new central version, then perhaps take over as the primary source of
614674
truth once again.
615675
616676
[bu]: ./backup.md
677
+[forks]: ./branching.wiki
617678
[setup]: ./caps/admin-v-setup.md#apsu
618679
[wflow]: ./concepts.wiki#workflow
619680
620681
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/
645773
646774
647775
<a id="trunk"></a>
648776
## The Main Branch Is Called "`trunk`"
649777
@@ -951,11 +1079,11 @@
9511079
We believe you get such answers to Git help requests in part
9521080
because of its lack of an always-up-to-date [index into its log](#log) and in
9531081
part because of its “small tools loosely joined” design philosophy. This
9541082
sort of command is therefore composed piece by piece:
9551083
956
-<center>◆  ◆  ◆</center>
1084
+<p style="text-align:center">◆  ◆  ◆</p>
9571085
9581086
“Oh, I know, I’ll search the rev-list, which outputs commit IDs by
9591087
parsing the log backwards from `HEAD`! Easy!”
9601088
9611089
git rev-list --before=2020-03-17
@@ -985,11 +1113,11 @@
9851113
9861114
git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master)
9871115
9881116
“Success, I guess?”
9891117
990
-<center>◆  ◆  ◆</center>
1118
+<p style="text-align:center">◆  ◆  ◆</p>
9911119
9921120
This vignette is meant to explain some of Git’s popularity: it rewards
9931121
the sort of people who enjoy puzzles, many of whom are software
9941122
developers and thus need a tool like Git. Too bad if you’re just a
9951123
normal user.
@@ -1185,11 +1313,11 @@
11851313
11861314
fossil ci
11871315
11881316
It’s one short command for Fossil instead of three for Git — or two if
11891317
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
11911319
[staging feature](#staging).
11921320
11931321
The “Friday afternoon sync-up” case is simpler, too:
11941322
11951323
fossil remote work
11961324
--- 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 @@
11
# File Name Glob Patterns
22
3
-
43
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
76
`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,
98
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
1110
directory prefix, and `*/*` matches a name with _one or more_
1211
directory components.
1312
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)
1514
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
1716
matching languages. Fossil uses globs when matching file names with the
1817
settings described in this document, not REs.
1918
2019
[glob]: https://en.wikipedia.org/wiki/Glob_(programming)
2120
[greinc]: https://unix.stackexchange.com/a/57958/138
2221
[regexp]: https://en.wikipedia.org/wiki/Regular_expression
2322
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
2524
give matching named files special treatment. Glob patterns are also
2625
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.
2829
2930
Where Fossil also accepts globs in commands, this handling may interact
3031
with your OS’s command shell or its C runtime system, because they may
3132
have their own glob pattern handling. We will detail such interactions
3233
below.
3334
3435
35
-## Syntax
36
+## <a id="syntax"></a>Syntax
3637
3738
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.
4656
4757
Most characters in a glob pattern consume a single character of the file
4858
name and must match it exactly. For instance, “a” in a glob simply
4959
matches the letter “a” in the file name unless it is inside a special
5060
character sequence.
@@ -61,11 +71,11 @@
6171
6272
Note that unlike [POSIX globs][pg], these special characters and
6373
sequences are allowed to match `/` directory separators as well as the
6474
initial `.` in the name of a hidden file or directory. This is because
6575
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.
6777
6878
[pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
6979
7080
The bracket expressions above require some additional explanation:
7181
@@ -163,14 +173,15 @@
163173
`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.
164174
`*/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.
165175
`*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`.
166176
`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.
167177
`*.[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
+
168179
169180
## Where Globs are Used
170181
171
-### Settings that are Globs
182
+### <a id="settings"></a>Settings that are Globs
172183
173184
These settings are all lists of glob patterns:
174185
175186
:Setting |:Description
176187
--------------------------------------------------------------------------------
@@ -197,12 +208,16 @@
197208
commands. This is especially true when one (or more) IDEs are used in
198209
a project because each IDE has its own ideas of how and where to cache
199210
information that speeds up its browsing and building tasks but which
200211
need not be preserved in your project's history.
201212
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.
202216
203
-### Commands that Refer to Globs
217
+
218
+### <a id="commands"></a>Commands that Refer to Globs
204219
205220
Many of the commands that respect the settings containing globs have
206221
options to override some or all of the settings. These options are
207222
usually named to correspond to the setting they override, such as
208223
`--ignore` to override the `ignore-glob` setting. These commands are:
@@ -227,29 +242,29 @@
227242
The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that
228243
implement or support with web servers provide a mechanism to name some
229244
files to serve with static content where a list of glob patterns
230245
specifies what content may be served.
231246
232
-[`add`]: /help?cmd=add
247
+[`add`]: /help?cmd=add
233248
[`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
251266
252267
253268
### Web Pages that Refer to Globs
254269
255270
The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that
@@ -263,19 +278,19 @@
263278
of a specific checkin. They may be further restricted by query
264279
parameters that specify glob patterns that name files to include or
265280
exclude rather than taking the entire checkin.
266281
267282
[`/timeline`]: /help?cmd=/timeline
268
-[`/tarball`]: /help?cmd=/tarball
269
-[`/zip`]: /help?cmd=/zip
283
+[`/tarball`]: /help?cmd=/tarball
284
+[`/zip`]: /help?cmd=/zip
270285
271286
272287
## Platform Quirks
273288
274289
Fossil glob patterns are based on the glob pattern feature of POSIX
275290
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
277292
patterns and quotes separately from Fossil, it is often difficult to
278293
give glob patterns correctly to Fossil on the command line. Quotes and
279294
special characters in glob patterns are likely to be interpreted when
280295
given as part of a `fossil` command, causing unexpected behavior.
281296
@@ -357,16 +372,16 @@
357372
358373
It bears repeating that the two glob patterns here are not interpreted
359374
the same way when running this command from a *subdirectory* of the top
360375
checkout directory as when running it at the top of the checkout tree.
361376
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:
364379
365380
$ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ*
366381
367
-instead. The Fossil glob pattern still needs the `doc/` prefix because
382
+The Fossil glob pattern still needs the `doc/` prefix because
368383
Fossil always interprets glob patterns from the base of the checkout
369384
directory, not from the current working directory as POSIX shells do.
370385
371386
When in doubt, use `fossil status` after running commands like the
372387
above to make sure the right set of files were scheduled for insertion
@@ -532,11 +547,13 @@
532547
`.fossil-settings/ignore-glob` may be possible in many cases. Here are
533548
some of features of `.gitignore` and comments on how they relate to
534549
Fossil:
535550
536551
* "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.
538555
* "Trailing spaces are ignored unless they are quoted..." is similar
539556
in Fossil. All whitespace before and after a glob is trimmed in
540557
Fossil unless quoted with single or double quotes. Git uses
541558
backslash quoting instead, which Fossil does not.
542559
* "An optional prefix "!" which negates the pattern...": not in
543560
--- 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
--- www/image-format-vs-repo-size.md
+++ www/image-format-vs-repo-size.md
@@ -1,19 +1,19 @@
11
# Image Format vs Fossil Repo Size
22
33
## The Problem
44
55
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]
77
That delta is then [zlib][zl]-compressed before being stored
88
in the Fossil repository database file.
99
1010
Storing pre-compressed data files in a Fossil repository defeats both of
1111
these space-saving measures:
1212
1313
1. Binary data compression algorithms turn the file data into
14
- [pseudorandom noise][prn].²
14
+ pseudorandom noise.[^prn]
1515
1616
Typical data compression algorithms are not [hash functions][hf],
1717
where the goal is that a change to each bit in the input has a
1818
statistically even chance of changing every bit in the output, but
1919
because they do approach that pathological condition, pre-compressed
@@ -34,11 +34,10 @@
3434
the input data change on each checkin. This article will illustrate that
3535
problem, quantify it, and give a solution to it.
3636
3737
[dc]: ./delta_format.wiki
3838
[hf]: https://en.wikipedia.org/wiki/Hash_function
39
-[prn]: https://en.wikipedia.org/wiki/Pseudorandomness
4039
[zl]: http://www.zlib.net/
4140
4241
4342
## <a id="formats"></a>Affected File Formats
4443
@@ -93,11 +92,11 @@
9392
image, check it in, and remember the new Fossil repo size.
9493
9594
5. Iterate on step 4 some number of times — currently 10 — and remember
9695
the Fossil repo size at each step.
9796
98
-6. Repeat the above steps for BMP, TIFF,³ and PNG.
97
+6. Repeat the above steps for BMP, PNG, and TIFF.[^tiff-cmp]
9998
10099
7. Create a bar chart showing how the Fossil repository size changes
101100
with each checkin.
102101
103102
We chose to use JupyterLab for this because it makes it easy for you to
@@ -115,20 +114,20 @@
115114
[wp]: http://wand-py.org/
116115
117116
118117
## <a id="results"></a>Results
119118
120
-Running the notebook gives a bar chart something like⁴ this:
119
+Running the notebook gives a bar chart something like[^variance] this:
121120
122121
![results bar chart](./image-format-vs-repo-size.svg)
123122
124123
There are a few key things we want to draw your attention to in that
125124
chart:
126125
127126
* BMP and uncompressed TIFF are nearly identical in size for all
128127
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
130129
feature: it is encoding each of those single-pixel changes in a very
131130
small amount of repository space.
132131
133132
* The JPEG and PNG bars increase by large amounts on most checkins
134133
even though each checkin *also* encodes only a *single-pixel change*.
@@ -158,11 +157,11 @@
158157
Since programs that produce and consume binary-compressed data files
159158
often make it either difficult or impossible to work with the
160159
uncompressed form, we want an automated method for producing the
161160
uncompressed form to make Fossil happy while still having the compressed
162161
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:
164163
165164
.SUFFIXES: .bmp .png .svg .svgz
166165
167166
.svgz.svg:
168167
gzip -dc < $< > $@
@@ -194,11 +193,11 @@
194193
195194
This `Makefile` allows you to treat the compressed version as the
196195
process input, but to actually check in only the changes against the
197196
uncompressed version by typing “`make`” before “`fossil ci`”. This is
198197
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
200199
before checking each change in anyway!
201200
202201
Because this technique is based on dependency rules, only the necessary
203202
files are generated on each `make` command.
204203
@@ -239,66 +238,65 @@
239238
automatically-uncompressed PDF for the benefit of Fossil. Unlike
240239
with the Excel case, there is no simple “file base name to directory
241240
name” mapping, so we just created the `-big` to `-small` name scheme
242241
here.
243242
-----
244243
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
249246
delta compression, so they’ll also be affected by this problem:
250247
[rsync][rs], [Unison][us], [Git][git], etc. You should take this
251248
article’s advice when using all such programs, not just Fossil.
252
-
253249
When using file copying and synchronization programs *without* delta
254250
compression, on the other hand, it’s best to use the most
255251
highly-compressed file format you can tolerate, since they copy the
256252
whole file any time any bit of it changes.
257253
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
259256
compression scheme is to run its output through the same sort of
260257
tests we use to gauge how “random” a given [PRNG][prng] is. Another
261258
way to look at it is that if there is a discernible pattern in the
262259
output of a compression scheme, that constitutes *information* (in
263260
[the technical sense of that word][ith]) that could be further
264261
compressed.
265262
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
267265
Zip-compressed TIFF, either of which would give similar results to
268266
PNG, which is always zlib-compressed.
269267
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
271270
use of random noise in the image to make the zlib/PNG compression
272271
more difficult, and the random pixel changes. Those test design
273272
choices make this a [Monte Carlo experiment][mce]. We’ve found that
274273
the overall character of the results doesn’t change from one run to
275274
the next.
276275
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
278278
and TIFF past the first commit. I suspect it is due to the SQLite
279279
indices being initialized for the first time.
280
-
281280
Page size inflation might have something to do with it as well,
282281
though we tried to control that by rebuilding the initial DB with a
283282
minimal page size. If you re-run the program often enough, you will
284283
sometimes see the BMP or TIFF bar jump higher than the other, again
285284
likely due to one of the repos crossing a page boundary.
286
-
287285
Another curious artifact in the data is that the BMP is slightly
288286
larger than for the TIFF. This goes against expectation because a
289287
low-tech format like BMP should have a small edge in this test
290288
because TIFF metadata includes the option for multiple timestamps,
291289
UUIDs, etc., which bloat the checkin size by creating many small
292290
deltas.
293291
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
295294
needed extensions [on the forum][for].
296295
297296
[for]: https://fossil-scm.org/forum/forumpost/15e677f2c8
298297
[git]: https://git-scm.com/
299298
[ith]: https://en.wikipedia.org/wiki/Information_theory
300299
[lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
301300
[prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator
302301
[rs]: https://rsync.samba.org/
303302
[us]: http://www.cis.upenn.edu/~bcpierce/unison/
304303
--- 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 ![results bar chart](./image-format-vs-repo-size.svg)
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 ![results bar chart](./image-format-vs-repo-size.svg)
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 @@
1414
<li> [./userlinks.wiki | User Links]
1515
<li> [./hacker-howto.wiki | Hacker How-To]
1616
<li> [./fossil-v-git.wiki | Fossil vs. Git]
1717
<li> [./permutedindex.html | Doc Index]
1818
</ul>
19
-<center><img src="fossil3.gif" align="center"></center>
19
+<p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p>
2020
</div>
2121
22
-<p>Fossil is a simple, high-reliability, distributed software configuration
22
+Fossil is a simple, high-reliability, distributed software configuration
2323
management system with these advanced features:
2424
2525
1. <b>Project Management</b> -
2626
In addition to doing [./concepts.wiki | distributed version control]
2727
like Git and Mercurial,
@@ -33,11 +33,11 @@
3333
2. <b>Built-in Web Interface</b> -
3434
Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible],
3535
and intuitive [./webui.wiki | web interface]
3636
with a rich variety of information pages
3737
([./webpage-ex.md|examples]) promoting situational awareness.
38
- <p>
38
+ <br><br>
3939
This entire website is just a running instance of Fossil.
4040
The pages you see here are all [./wikitheory.wiki | wiki] or
4141
[./embeddeddoc.wiki | embedded documentation] or (in the case of
4242
the [/uv/download.html|download] page)
4343
[./unvers.wiki | unversioned files].
@@ -83,16 +83,16 @@
8383
the repository are consistent prior to each commit.
8484
8585
8. <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license].
8686
8787
<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>
8989
9090
* [/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]
9494
* [/timeline?t=release|Timeline of all past releases]
9595
9696
<hr>
9797
<h3>Quick Start</h3>
9898
9999
--- 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
--- www/mirrortogithub.md
+++ www/mirrortogithub.md
@@ -2,83 +2,76 @@
22
33
Beginning with Fossil version 2.9, you can mirror a Fossil-based
44
project on GitHub (with [limitations](./mirrorlimitations.md))
55
by following these steps:
66
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
98
into that account.
109
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
1211
your project with various things like a README file. Answer "no" to
1312
everything. You want a completely blank project. GitHub will then
1413
supply you with a URL for your project that will look something
1514
like this:
1615
17
-<blockquote>
18
-https://github.com/username/project.git
19
-</blockquote>
16
+ https://github.com/username/project.git
2017
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
2219
project and type:
2320
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&nbsp;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&nbsp;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
5958
mirrored on GitHub.
6059
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
6261
the mirror:
6362
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
8073
8174
## Notes:
8275
8376
* Unless you specify --force, the mirroring only happens if the Fossil
8477
repo has changed, with Fossil reporting "no changes", because Fossil
8578
--- 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&nbsp;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&nbsp;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 @@
153153
set out [open permutedindex.html w]
154154
fconfigure $out -encoding utf-8 -translation lf
155155
puts $out \
156156
"<div class='fossil-doc' data-title='Index Of Fossil Documentation'>"
157157
puts $out {
158
-<center>
159
-<form action='$ROOT/docsrch' method='GET'>
158
+<form action='$ROOT/docsrch' method='GET' style="text-align:center">
160159
<input type="text" name="s" size="40" autofocus>
161160
<input type="submit" value="Search Docs">
162161
</form>
163
-</center>
164162
<h2>Primary Documents:</h2>
165163
<ul>
166164
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
167165
<li> <a href='$ROOT/help'>Built-in help for commands and webpages</a>
168166
<li> <a href='history.md'>Purpose and History of Fossil</a>
169167
--- 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
--- www/newrepo.wiki
+++ www/newrepo.wiki
@@ -1,12 +1,12 @@
11
<title>How To Create A New Fossil Repository</title>
22
3
-<p> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how
3
+The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how
44
to get up and running with fossil. But once you're running, what can
55
you do with it? This document will walk you through the process of
66
creating a fossil repository, populating it with files, and then
7
-sharing it over the web.</p>
7
+sharing it over the web.
88
99
The first thing we need to do is create a fossil repository file:
1010
1111
<verbatim>
1212
stephan@ludo:~/fossil$ fossil new demo.fossil
1313
--- 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
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -1,13 +1,11 @@
11
<div class='fossil-doc' data-title='Index Of Fossil Documentation'>
22
3
-<center>
4
-<form action='$ROOT/docsrch' method='GET'>
3
+<form action='$ROOT/docsrch' method='GET' style="text-align:center">
54
<input type="text" name="s" size="40" autofocus>
65
<input type="submit" value="Search Docs">
76
</form>
8
-</center>
97
<h2>Primary Documents:</h2>
108
<ul>
119
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
1210
<li> <a href='$ROOT/help'>Built-in help for commands and webpages</a>
1311
<li> <a href='history.md'>Purpose and History of Fossil</a>
1412
--- 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 @@
11
<title>Principles Of Operation</title>
22
<h1 align="center">Principles Of Operation</h1>
33
4
-<p>
54
This page attempts to define the foundational principals upon
65
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.
7774
--- 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 @@
11
<title>Questions And Criticisms</title>
22
<nowiki>
33
<h1 align="center">Questions And Criticisms</h1>
44
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
66
raised against Fossil early in its history (circa 2008).
77
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>.
99
1010
<b>Fossil sounds like a lot of reinvention of the wheel.
1111
Why create your own DVCS when you could have reused mercurial?</b>
1212
1313
<blockquote>
14
- <p>I wrote fossil because none of the
14
+ I wrote fossil because none of the
1515
other available DVCSes met my needs. If the other DVCSes do
1616
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.
1818
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:
2121
2222
<ol>
2323
<li> Integrated <a href="wikitheory.wiki">wiki</a>. </li>
2424
<li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li>
2525
<li> Immutable artifacts </li>
@@ -67,30 +67,30 @@
6767
6868
<b>Fossil looks like the bug tracker that would be in your
6969
Linksys Router's administration screen.</b>
7070
7171
<blockquote>
72
-<p>I take a pragmatic approach to software: form follows function.
72
+I take a pragmatic approach to software: form follows function.
7373
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.
7575
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
7777
of Fossil without seriously compromising its reliability, performance,
7878
and/or maintainability, I will be happy to accept them. Fossil is
7979
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.
8181
</blockquote>
8282
8383
<b>It would be useful to have a separate application that
8484
keeps the bug-tracking database in a versioned file. That file can
8585
then be pushed and pulled along with the rest repository.</b>
8686
8787
<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
8989
in your repository.
9090
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:
9292
9393
<ol>
9494
<li> Check-ins in fossil are immutable. So if
9595
tickets were part of the check-in, then there would be no way to add
9696
new tickets to a check-in as new bugs are discovered.
@@ -105,12 +105,12 @@
105105
of tickets to developers with check-in privileges and an installed
106106
copy of the fossil executable. Casual passers-by on the internet should
107107
be permitted to create tickets.
108108
</ol>
109109
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.
112112
</blockquote>
113113
114114
<b>Fossil is already the name of a plan9 versioned
115115
append-only filesystem.</b>
116116
@@ -136,24 +136,24 @@
136136
directly in the VCS - either they are under-featured compared to full
137137
software like Trac, or the VCS is massively bloated compared to
138138
Subversion or Bazaar.</b>
139139
140140
<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
142142
is not the point. Fossil has several key features that Trac lacks and that
143143
I need: most notably the fact that
144
-fossil supports disconnected operation.</p>
144
+fossil supports disconnected operation.
145145
146
-<p>As for bloat: Fossil is a single self-contained executable.
146
+As for bloat: Fossil is a single self-contained executable.
147147
You do not need any other packages
148148
(diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache,
149149
sqlite, and so forth)
150150
in order to run fossil. Fossil runs just fine in a chroot jail all
151151
by itself. And the self-contained fossil
152152
executable is much less than 1MB in size. (Update 2015-01-12: Fossil has
153153
grown in the years since the previous sentence was written but is still
154154
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.
156156
</blockquote>
157157
158158
159159
</nowiki>
160160
--- 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 @@
11
<title>Fossil Quick Start Guide</title>
22
<h1 align="center">Fossil Quick Start</h1>
33
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.
66
77
<h2 id="install">Installing</h2>
88
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
1010
either download a
1111
[https://fossil-scm.org/home/uv/download.html|precompiled
1212
binary]
1313
or <a href="build.wiki">compile it yourself</a> from sources.
1414
Install Fossil by putting the fossil binary
15
-someplace on your $PATH.</p>
15
+someplace on your $PATH.
16
+
1617
You can test that Fossil is present and working like this:
1718
1819
<blockquote>
1920
<b>
2021
fossil version<br>
@@ -22,15 +23,15 @@
2223
</b>
2324
</blockquote>
2425
2526
<h2 id="workflow" name="fslclone">General Work Flow</h2>
2627
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
2829
complete history) and with checked-out local trees (the working directory
2930
you use to do your work).
3031
(See [./glossary.md | the glossary] for more background.)
31
-The workflow looks like this:</p>
32
+The workflow looks like this:
3233
3334
<ul>
3435
<li>Create or clone a repository file. ([/help/init|fossil init] or
3536
[/help/clone | fossil clone])
3637
<li>Check out a local tree. ([/help/open | fossil open])
@@ -39,17 +40,17 @@
3940
</ul>
4041
4142
Fossil can be entirely driven from the command line. Many features
4243
can also be conveniently accessed from the built-in web user interface.
4344
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.
4647
4748
<h2 id="new">Starting A New Project</h2>
4849
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])
5152
5253
<blockquote>
5354
<b>fossil init </b><i> repository-filename</i>
5455
</blockquote>
5556
@@ -57,23 +58,23 @@
5758
The <tt>.fossil</tt> extension is traditional but only required if you are going to use the
5859
<tt>[/help/server | fossil server DIRECTORY]</tt> feature.”
5960
6061
<h2 id="clone">Cloning An Existing Repository</h2>
6162
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
6364
local disk drive, not on a remote system. Hence, before accessing
6465
a remote repository it is necessary to make a local copy of that
6566
repository. Making a local copy of a remote repository is called
66
-"cloning".</p>
67
+"cloning".
6768
68
-<p>Clone a remote repository as follows: ([/help/clone | more info])</p>
69
+Clone a remote repository as follows: ([/help/clone | more info])
6970
7071
<blockquote>
7172
<b>fossil clone</b> <i>URL repository-filename</i>
7273
</blockquote>
7374
74
-<p>The <i>URL</i> specifies the fossil repository
75
+The <i>URL</i> specifies the fossil repository
7576
you want to clone. The <i>repository-filename</i> is the new local
7677
filename into which the cloned repository will be written. For
7778
example, to clone the source code of Fossil itself:
7879
7980
<blockquote>
@@ -94,25 +95,25 @@
9495
server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br>
9596
admin-user: exampleuser (password is "yoWgDR42iv")><br>
9697
</tt></b>
9798
</blockquote>
9899
99
-<p>If the remote repository requires a login, include a
100
+If the remote repository requires a login, include a
100101
userid in the URL like this:
101102
102103
<blockquote>
103104
<b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b>
104105
</blockquote>
105106
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,
114115
you can just make a copy of the repository file (for example, using
115116
"scp"). Note, however, that the repository file contains auxiliary
116117
information above and beyond the versioned files, including some
117118
sensitive information such as password hashes and email addresses. If you
118119
want to share Fossil repositories directly by copying, consider running the
@@ -119,11 +120,11 @@
119120
[/help/scrub|fossil scrub] command to remove sensitive information
120121
before transmitting the file.
121122
122123
<h2 id="import">Importing From Another Version Control System</h2>
123124
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,
125126
you might prefer to
126127
<a href="./inout.wiki">import an existing Git project</a>
127128
into Fossil using the [/help/import | fossil import] command.
128129
129130
You can even decide to export your project back into git using the
@@ -137,14 +138,14 @@
137138
[https://www.mercurial-scm.org/|Mercurial].
138139
Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.
139140
140141
<h2 id="checkout">Checking Out A Local Tree</h2>
141142
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
143144
copy of the source tree. Create the directory you want to be
144145
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])
146147
147148
<blockquote>
148149
<b>fossil open </b><i> repository-filename</i>
149150
</blockquote>
150151
@@ -160,15 +161,15 @@
160161
</tt></b>
161162
</blockquote>
162163
163164
(or "fossil open ..\myclone.fossil" on Windows).
164165
165
-<p>This leaves you with the newest version of the tree
166
+This leaves you with the newest version of the tree
166167
checked out.
167168
From anywhere underneath the root of your local tree, you
168169
can type commands like the following to find out the status of
169
-your local tree:</p>
170
+your local tree:
170171
171172
<blockquote>
172173
<b>[/help/info | fossil info]</b><br>
173174
<b>[/help/status | fossil status]</b><br>
174175
<b>[/help/changes | fossil changes]</b><br>
@@ -176,60 +177,63 @@
176177
<b>[/help/timeline | fossil timeline]</b><br>
177178
<b>[/help/ls | fossil ls]</b><br>
178179
<b>[/help/branch | fossil branch]</b><br>
179180
</blockquote>
180181
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.
183184
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
185186
separate directories from the same repository. This enables you,
186187
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.
188189
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:<
191192
192193
<blockquote>
193194
<b>[/help/update | fossil update]</b><br>
194195
<b>[/help/checkout | fossil checkout]</b><br>
195196
</blockquote>
196197
197
-<p>[/help/update | update] honors the "autosync" option and
198
+[/help/update | update] honors the "autosync" option and
198199
does a "soft" switch, merging any local changes into the target
199200
version, whereas [/help/checkout | checkout] does not
200201
automatically sync and does a "hard" switch, overwriting local
201
-changes if told to do so.</p>
202
+changes if told to do so.
202203
203204
<h2 id="changes">Making and Committing Changes</h2>
204205
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:
207208
208209
<blockquote>
209210
<b>[/help/add | fossil add]</b> <i>file...</i><br>
210211
<b>[/help/rm | fossil rm]</b> <i>file...</i><br>
211212
<b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
212213
</blockquote>
213214
214
-<p>The command:</p>
215
+The command:
216
+
215217
<blockquote>
216218
<b>
217219
[/help/changes | fossil changes]</b>
218220
</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":
221224
222225
<blockquote>
223226
<b>
224227
fossil changes<br>
225228
EDITED README.md
226229
</b>
227230
</blockquote>
228231
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
+
231235
<blockquote>
232236
<b>
233237
fossil diff <br><tt>
234238
Index: README.md<br>
235239
============================================================<br>
@@ -239,17 +243,19 @@
239243
+Made some changes to the project<br>
240244
# Original text<br>
241245
</tt></b>
242246
</blockquote>
243247
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.
247253
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
249255
find out the most recent commit, and then "fossil diff" between that commit and the
250
-current tree: </p>
256
+current tree:
251257
<blockquote>
252258
<b>
253259
fossil timeline <br><tt>
254260
=== 2021-03-28 === <br>
255261
03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br>
@@ -269,11 +275,11 @@
269275
</blockquote>
270276
271277
"current" is an alias for the checkout version, so the command
272278
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
273279
274
-<p>To commit your changes to a local-only repository:</p>
280
+To commit your changes to a local-only repository:
275281
<blockquote>
276282
<b>
277283
fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt>
278284
# Enter a commit message for this check-in. Lines beginning with # are ignored.<br>
279285
#<br>
@@ -284,163 +290,136 @@
284290
Edited file to add description of code changes<br>
285291
New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br>
286292
</tt></b>
287293
</blockquote>
288294
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
290296
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.
292298
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.
298305
299306
<h2 id="naming">Naming of Files, Checkins, and Branches</h2>
300307
301
-<p>Fossil deals with information artifacts. This Quickstart document only deals
308
+Fossil deals with information artifacts. This Quickstart document only deals
302309
with files and collections of files, but be aware there are also tickets, wiki pages and more.
303310
Every artifact in Fossil has a universally-unique hash id, and may also have a
304
-human-readable name.</p>
311
+human-readable name.
305312
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:
308315
309316
<ul>
310317
<li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a
311318
<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
312319
<li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash
313320
</ul>
314321
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
316323
the first checkin, and the default for any time a branch name is needed but not
317
-specified.</p>
324
+specified.
318325
319326
This will get you started on identifying checkins. The
320327
<a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
321328
how timestamps can also be used.
322329
323330
<h2 id="config">Configuring Your Local Repository</h2>
324331
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
326333
project or create a new project of your own, you usually want to do some
327334
local configuration. This is easily accomplished using the web-server
328335
that is built into fossil. Start the fossil web server like this:
329
-([/help/ui | more info])</p>
336
+([/help/ui | more info])
330337
331338
<blockquote>
332339
<b>fossil ui </b><i> repository-filename</i>
333340
</blockquote>
334341
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.
337344
338
-<p>This starts a web server then automatically launches your
345
+This starts a web server then automatically launches your
339346
web browser and makes it point to this web server. If your system
340347
has an unusual configuration, fossil might not be able to figure out
341348
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:
343350
344351
<blockquote>
345352
<b>fossil setting web-browser </b><i> path-to-web-browser</i>
346353
</blockquote>
347354
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
349356
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.
382361
383362
<h2 id="sharing">Sharing Changes</h2>
384363
385
-<p>When [./concepts.wiki#workflow|autosync] is turned off,
364
+When [./concepts.wiki#workflow|autosync] is turned off,
386365
the changes you [/help/commit | commit] are only
387366
on your local repository.
388
-To share those changes with other repositories, do:</p>
367
+To share those changes with other repositories, do:
389368
390369
<blockquote>
391370
<b>[/help/push | fossil push]</b> <i>URL</i>
392371
</blockquote>
393372
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
395374
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.
397376
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
399378
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]:
401380
402381
<blockquote>
403382
<b>[/help/pull | fossil pull]</b> <i>URL</i><br>
404383
<b>[/help/sync | fossil sync]</b> <i>URL</i>
405384
</blockquote>
406385
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,
408387
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]:
410389
411390
<blockquote>
412391
<b>[/help/update | fossil update]</b> <i>VERSION</i>
413392
</blockquote>
414393
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
416395
abbreviation to the 40-character
417396
artifact identifier for a particular check-in, or it can be a
418397
date/time stamp. ([./checkin_names.wiki | more info])
419398
If you omit
420399
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.
422401
423
-<p>The default behavior is for [./concepts.wiki#workflow|autosync] to
402
+The default behavior is for [./concepts.wiki#workflow|autosync] to
424403
be turned on. That means that a [/help/pull|pull] automatically occurs
425404
when you run [/help/update|update] and a [/help/push|push] happens
426405
automatically after you [/help/commit|commit]. So in normal practice,
427406
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.
429408
430409
<blockquote>
431410
<b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
432411
</blockquote>
433412
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
435414
setting, nor does it merge in local changes - it prefers to overwrite
436415
them and fails if local changes exist unless the <tt>--force</tt>
437
-flag is used.</p>
416
+flag is used.
438417
439418
<h2 id="branch" name="merge">Branching And Merging</h2>
440419
441
-<p>Use the --branch option to the [/help/commit | commit] command
420
+Use the --branch option to the [/help/commit | commit] command
442421
to start a new branch. Note that in Fossil, branches are normally
443422
created when you commit, not before you start editing. You can
444423
use the [/help/branch | branch new] command to create a new branch
445424
before you start editing, if you want, but most people just wait
446425
until they are ready to commit.
@@ -447,25 +426,25 @@
447426
448427
To merge two branches back together, first
449428
[/help/update | update] to the branch you want to merge into.
450429
Then do a [/help/merge|merge] of the other branch that you want to incorporate
451430
the changes from. For example, to merge "featureX" changes into "trunk"
452
-do this:</p>
431
+do this:
453432
454433
<blockquote>
455434
<b>fossil [/help/update|update] trunk</b><br>
456435
<b>fossil [/help/merge|merge] featureX</b><br>
457436
<i># make sure the merge didn't break anything...</i><br>
458437
<b>fossil [/help/commit|commit]
459438
</blockquote>
460439
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
462441
version identifier forms that work for [/help/update|update].
463442
([./checkin_names.wiki|more info].)
464443
The merge command has options to cherry-pick individual
465444
changes, or to back out individual changes, if you don't want to
466
-do a full merge.</p>
445
+do a full merge.
467446
468447
The merge command puts all changes in your working check-out.
469448
No changes are made to the repository.
470449
You must run [/help/commit|commit] separately
471450
to add the merge changes into your repository to make them persistent
@@ -478,110 +457,110 @@
478457
multiple merges. So even if you have merged the featureX branch
479458
into trunk previously, you can do so again and Fossil will automatically
480459
know to pull in only those changes that have occurred since the previous
481460
merge.
482461
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:
485464
486465
<blockquote>
487466
<b>[/help/undo | fossil undo]</b>
488467
</blockquote>
489468
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
491470
working checkout. There is also a [/help/redo|redo] command if you undo by
492471
mistake. Undo and redo only work for changes that have
493472
not yet been checked in using commit and there is only a single
494
-level of undo/redo.</p>
473
+level of undo/redo.
495474
496475
497476
<h2 id="server">Setting Up A Server</h2>
498477
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:
501480
502481
<blockquote>
503482
<b>[/help/server | fossil server]</b> <i>repository-filename</i><br>
504483
<b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
505484
</blockquote>
506485
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
508487
are run from within an open check-out, which is a particularly useful
509488
shortcut with the <b>fossil ui</b> command.
510489
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
512491
from a local desktop. (We sometimes call this mode "Fossil UI.")
513492
The <b>ui</b> command differs from the
514493
<b>server</b> command by binding to the loopback IP
515494
address only (thus making the web UI visible only on the
516495
local machine) and by automatically starting your default web browser,
517496
pointing it at the running UI
518497
server. The localhost restriction exists because it also gives anyone
519498
who can access the resulting web UI full control over the
520499
repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful
521
-Setup capabliity].)</p>
500
+Setup capabliity].)
522501
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,
524503
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].
526505
527
-<p>Servers are also easily configured as:
506
+Servers are also easily configured as:
528507
529508
<ul>
530509
<li>[./server/any/inetd.md|inetd]
531510
<li>[./server/debian/service.md|systemd]
532511
<li>[./server/any/cgi.md|CGI]
533512
<li>[./server/any/scgi.md|SCGI]
534513
</ul>
535514
536
-<p>…along with [./server/#matrix | several other options].</p>
515
+…along with [./server/#matrix | several other options].
537516
538
-<p>The [./selfhost.wiki | self-hosting fossil repositories] use
517
+The [./selfhost.wiki | self-hosting fossil repositories] use
539518
CGI.
540519
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
542521
not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server]
543
-article for details.</p>
522
+article for details.
544523
545524
<h2 id="proxy">HTTP Proxies</h2>
546525
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
548527
an HTTP proxy to reach the internet, then you can configure the proxy
549528
in three different ways. You can tell fossil about your proxy using
550529
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>.
552531
553532
<blockquote>
554533
<b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i>
555534
</blockquote>
556535
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
558537
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:
560539
561540
<blockquote>
562541
<b>fossil setting proxy </b><i>Proxy-URL</i>
563542
</blockquote>
564543
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:
566545
567546
<blockquote>
568547
<b>export http_proxy=</b><i>Proxy-URL</i>
569548
</blockquote>
570549
571
-<p>To stop using the proxy, do:</p>
550
+To stop using the proxy, do:
572551
573552
<blockquote>
574553
<b>fossil setting proxy off</b>
575554
</blockquote>
576555
577
-<p>Or unset the environment variable. The fossil setting for the
556
+Or unset the environment variable. The fossil setting for the
578557
HTTP proxy takes precedence over the environment variable and the
579558
command-line option overrides both. If you have a persistent
580559
proxy setting that you want to override for a one-time sync, that
581560
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:
583562
584563
<blockquote>
585564
<b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
586565
</blockquote>
587566
588567
--- 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 @@
145145
</blockquote>
146146
147147
<li>In the fossil community - and hence in fossil itself - development history
148148
is pretty much sacrosanct. The very name "fossil" was to chosen to
149149
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
152152
the published aspect of the project, so it provides tools for rearranging
153153
that history so you can present what you "should" have done rather
154154
than what you actually did.
155155
156156
<blockquote>
157157
--- 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
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -198,15 +198,16 @@
198198
199199
But that argument is comparing apples to oranges, since the two diffs
200200
do not have the same baseline. The correct way to see only the feature
201201
branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7).
202202
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">
204205
<tr><th>Rebase<th>Merge<th>What You See
205206
<tr><td>diff(C2,C5\')<td>diff(C2,C7)<td>Commingled branch and mainline changes
206207
<tr><td>diff(C6,C5\')<td>diff(C6,C7)<td>Branch changes only
207
-</table></center>
208
+</table>
208209
209210
Remember: C7 and C5\' are bit-for-bit identical, so the output of the
210211
diff is not determined by whether you select C7 or C5\' as the target
211212
of the diff, but rather by your choice of the diff source, C2 or C6.
212213
213214
--- 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
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -6,14 +6,14 @@
66
security, ease of configuration, and low resource usage.
77
88
To set up a Fossil server as CGI on a host running the althttpd web
99
server, follow these steps.
1010
<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
1212
done by following the [althttpd documentation][althttpd].
1313
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
1515
be typically be two lines of code that look something like this:
1616
1717
~~~
1818
#!/usr/bin/fossil
1919
repository: /home/yourlogin/fossils/project.fossil
@@ -23,13 +23,13 @@
2323
CGI script accepts [other options][cgi] besides the
2424
repository:" line. You can add in other options as you desire,
2525
but the single "repository:" line is normally all that is needed
2626
to get started.
2727
28
-<li><p>Make the CGI script executable.
28
+<li>Make the CGI script executable.
2929
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
3131
the repository are both writable by whatever user the web server is
3232
running and.
3333
</ol>
3434
3535
And you are done. Visit the URL that corresponds to the CGI script
3636
--- 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
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -142,46 +142,79 @@
142142
contains the configuration for `*.foo.net`.
143143
144144
The configuration for our `example.com` web site, stored in
145145
`/etc/nginx/sites-enabled/local/example.com` is:
146146
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
+----
177195
178196
As you can see, this is a pure extension of [the basic nginx service
179197
configuration for SCGI][scgii], showing off a few ideas you might want to
180198
try on your own site, such as static asset proxying.
181199
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
183216
repetition among the multiple sites this configuration hosts:
184217
185218
root /var/www/$host;
186219
187220
listen 80;
@@ -189,12 +222,12 @@
189222
190223
charset utf-8;
191224
192225
There are some configuration directives that nginx refuses to substitute
193226
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
196229
pattern from one host to the next. Sadly, you must tolerate some
197230
repetition across `server { }` blocks when setting up multiple domains
198231
on a single server.
199232
200233
The configuration for `foo.net` is similar.
201234
--- 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
--- www/server/debian/service.md
+++ www/server/debian/service.md
@@ -12,16 +12,18 @@
1212
1313
Two of the methods for running [containerized Fossil][cntdoc] integrate
1414
with `systemd`, potentially obviating the more direct methods below:
1515
1616
* 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.
2325
2426
* If you’re willing to give up [a lot of features][nsweak] relative to
2527
Podman, and you’re willing to tolerate a lot more manual
2628
administrivia, [the nspawn method][nspawn] has a lot less overhead,
2729
being a direct feature of `systemd` itself.
@@ -34,11 +36,10 @@
3436
[dhrepo]: https://hub.docker.com/r/tangentsoft/fossil
3537
[fslsrv]: https://tangentsoft.com/fossil/dir?name=bin
3638
[nspawn]: ../../containers.md#nspawn
3739
[nsweak]: ../../containers.md#nspawn-weaknesses
3840
[podman]: ../../containers.md#podman
39
-[tscnt]: https://tangentsoft.com/fossil/dir/container
4041
4142
4243
## User Service
4344
4445
A fun thing you can easily do with `systemd` that you can’t directly do
@@ -202,17 +203,15 @@
202203
203204
[Install]
204205
WantedBy=multi-user.target
205206
```
206207
207
-We’ll explain the “`@`” in the file name below.
208
-
209208
Notice that we haven’t told `systemd` which user and group to run Fossil
210209
under. Since this is a system-level service definition, that means it
211210
will run as root, which then causes Fossil to [automatically drop into a
212211
`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.
214213
215214
The `Restart*` directives we had in the user service configuration above
216215
are unnecessary for this method, since Fossil isn’t supposed to remain
217216
running under it. Each HTTP hit starts one Fossil instance, which
218217
handles that single client’s request and then immediately shuts down.
219218
--- 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
--- www/server/index.html
+++ www/server/index.html
@@ -117,11 +117,11 @@
117117
<li><a href="any/scgi.md">SCGI</a>
118118
<li><a href="#ssh">SSH</a>
119119
</ol>
120120
121121
<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>
123123
124124
<p>You are not restricted to a single server setup. The same Fossil
125125
repository can be served using two or more of the above techniques at
126126
the same time. These methods use clean, well-defined, standard
127127
interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from
@@ -171,11 +171,11 @@
171171
dispatches a copy of itself to deal with each incoming request. You can
172172
expose Fossil directly to the clients in this way or you can interpose a
173173
<a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
174174
layer between the clients and Fossil.</p>
175175
176
-<h3 id="scgi"3>SCGI</h3>
176
+<h3 id="scgi">SCGI</h3>
177177
178178
<p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
179179
When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is
180180
run with the extra <tt>--scgi</tt> option, it listens for incoming
181181
SCGI requests rather than HTTP requests. This allows Fossil to
182182
--- 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
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -12,11 +12,12 @@
1212
<h2>But, a Server Can Be Useful</h2>
1313
1414
Fossil does not require a server, but a server can be very useful.
1515
Here are a few reasons to set up a Fossil server for your project:
1616
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
+
1819
Fossil does more than just version control. It also supports
1920
[../tickets.wiki|trouble-tickets],
2021
[../wikitheory.wiki|wiki],
2122
a [../chat.md|developer chat room], and a [../forum.wiki|forum].
2223
The [../embeddeddoc.wiki|embedded documentation]
@@ -23,11 +24,12 @@
2324
feature provides a great mechanism for providing project documentation.
2425
The [../unvers.wiki|unversioned files] feature is a convenient way
2526
to host builds and downloads on the project website.
2627
2728
2. <b>A server gives developers a common point of rendezvous for
28
- syncing their work.</b><p>
29
+ syncing their work.</b>
30
+
2931
It is possible for developers to synchronize peer-to-peer but
3032
that requires the developers coordinate the sync, which in turn
3133
requires that the developers both want to sync at the same moment.
3234
A server alleviates this time dependency by allowing each developer
3335
to sync whenever it is convenient. For example, a developer may
@@ -34,52 +36,60 @@
3436
choose to automatically sync
3537
after each commit and before each update. Developers all stay
3638
in sync with each other without having to interrupt each other
3739
constantly to set up a peer-to-peer sync.
3840
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
+
4043
Project coordinators and BDFLs can click on a link or two at the
4144
central Fossil server for a project and quickly tell what is
4245
going on. They can do this from anywhere — even from their phones
4346
— without needing to actually sync to the device they are using.
4447
45
- 4. <b>A server provides automatic off-site backups.</b><p>
48
+ 4. <b>A server provides automatic off-site backups.</b>
49
+
4650
A Fossil server is an automatic remote backup for all the work
4751
going into a project. ([../backup.md | Within limits].)
4852
You can even set up multiple servers at
4953
multiple sites with automatic synchronization between them for
5054
added redundancy. Such a setup means that no work is lost due
5155
to a single machine failure.
5256
5357
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
+
5560
The concerns in section 1 of that document assume you have direct
5661
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
+
5864
Section 2 is about file locking, which concerns disappear when Fossil's
5965
on the other side of an HTTP boundary and your server is set up
60
- properly.<p>
66
+ properly.
67
+
6168
Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations,
6269
but setting up a server lets you address the risks
6370
in a single place. Once a given commit is
6471
sync'd to the server, you can be reasonably sure any client-side
6572
corruption can be fixed with a fresh clone. Ultimately, this
6673
is an argument for off-machine backups, which returns us to reason
67
- #4 above.<p>
74
+ #4 above.
75
+
6876
Sections 3.2 and the entirety of section 7 are no concern with
6977
Fossil at all, since it's primarily written by the creator and
7078
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
7382
"[https://sqlite.org/useovernet.html | SQLite Over a Network,
7483
Caveats and Considerations]". Fossil runs in rollback mode by
7584
default per recommendation #3 at the end of that article, and a
7685
Fossil server operates as a network proxy for the underlying
7786
SQLite repository DB per recommendation #2. This <i>may</i> permit
7887
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.
8089
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
+
8292
The role-based access control (RBAC) system in Fossil only works
8393
when the remote system is on the other side of an HTTP barrier.
8494
([../caps/#webonly | Details].) If you want its benefits, you need
8595
a Fossil server setup of some kind.
8696
--- 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
--- www/settings.wiki
+++ www/settings.wiki
@@ -8,11 +8,11 @@
88
99
For a list of all settings, view the Settings page, or type
1010
<tt>fossil help settings</tt> from the command line.
1111
1212
13
-<h3>Repository settings</h3>
13
+<h3 id="repo">Repository settings</h3>
1414
1515
Settings are set on a per-repository basis. When you clone a repository,
1616
a subset of settings are copied to your local repository.
1717
1818
If you make a change to a setting on your local repository, it is not
@@ -24,11 +24,11 @@
2424
will be used for all repositories cloned to your machine, unless
2525
overridden explicitly in a particular repository. Global settings can be
2626
set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
2727
command.
2828
29
-<h3>"Versionable" settings</h3>
29
+<h3 id="versionable">"Versionable" settings</h3>
3030
3131
Most of the settings control the behaviour of fossil on your local
3232
machine, largely acting to reflect your preference on how you want to
3333
use Fossil, how you communicate with the server, or options for hosting
3434
a repository on the web.
3535
--- 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 @@
3737
Fossil to insert bad control artifacts. Therefore, before we get to
3838
methods of permanently deleting content from a Fossil repos, let's give
3939
some alternatives that usually suffice, which don't damage the project's
4040
fossil record:
4141
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.
9291
9392
<h2>Exception: Non-versioned Content</h2>
9493
9594
It is normal and expected to delete data which is not versioned, such as
9695
usernames and passwords in the user table. The [/help/scrub|fossil scrub]
9796
--- 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 @@
140140
</pre>
141141
142142
Fossil relies on the OpenSSL library to have some way to check a trusted
143143
list of CA signing keys. There are two common ways this fails:
144144
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
146146
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.
150151
151152
A common reason to fall into the second trap is that you're using
152153
certificates signed by a local private CA, as often happens in large
153154
enterprises. You can solve this sort of problem by getting your local
154155
CA's signing certificate in PEM format and pointing OpenSSL at it:
@@ -272,26 +273,28 @@
272273
it issues a redirect...'round and 'round it goes until the web browser
273274
detects it's in a redirect loop and gives up. This problem prevents you
274275
from getting back into the Admin UI to fix it, but there are several
275276
ways to fix it:
276277
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
278279
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
281283
repository, so if you make a backup first <i>on the server</i>, you
282284
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
285288
repository file down to a local machine, use <tt>fossil ui</tt> to
286289
fix the setting, and then upload it to the repository server
287
- again.</p>
290
+ again.
288291
289292
It's best to enforce TLS-only access at the front-end proxy level
290293
anyway. It not only avoids the problem entirely, it can be significantly
291294
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.
293296
294297
295298
<h2>Terminology Note</h2>
296299
297300
This document is called <tt>ssl.wiki</tt> for historical reasons. The
298301
--- 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 @@
11
<title>The Fossil Sync Protocol</title>
22
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.
55
66
<h2>1.0 Overview</h2>
77
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
99
collection of artifacts. Each artifact is identified by a cryptographic
1010
hash of its content, expressed as a lower-case hexadecimal string.
1111
Synchronization is the process of sharing artifacts between
1212
repositories so that all repositories have copies of all artifacts. Because
1313
artifacts are unordered, the order in which artifacts are received
@@ -17,13 +17,13 @@
1717
of hashes for available artifacts, then sharing the content of artifacts
1818
whose names are missing from one side or the other of the connection.
1919
In practice, a repository might contain millions of artifacts. The list of
2020
hash names for this many artifacts can be large. So optimizations are
2121
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.
2323
24
-<p>Each repository also has local state. The local state determines
24
+Each repository also has local state. The local state determines
2525
the web-page formatting preferences, authorized users, ticket formats,
2626
and similar information that varies from one repository to another.
2727
The local state is not usually transferred during a sync. Except,
2828
some local state is transferred during a [/help?cmd=clone|clone]
2929
in order to initialize the local state of the new repository. Also,
@@ -32,67 +32,67 @@
3232
[/help?cmd=configuration|config pull]
3333
commands.
3434
3535
<h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3>
3636
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
3838
implementation of a particular
3939
[https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free
4040
Replicated Datatype (CRDT)] called a "G-Set" or "Grow-only Set". The
4141
academic literature on CRDTs only began to appear in about 2011, and
4242
Fossil predates that research by at least 4 years. But it is nice to
4343
know that theorists have now proven that the underlying data model of
4444
Fossil can provide strongly-consistent replicas using only
4545
peer-to-peer communication and without any kind of central
46
-authority.</p>
46
+authority.
4747
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.
5050
5151
5252
<h2>2.0 Transport</h2>
5353
54
-<p>All communication between client and server is via HTTP requests.
54
+All communication between client and server is via HTTP requests.
5555
The server is listening for incoming HTTP requests. The client
5656
issues one or more HTTP requests and receives replies for each
57
-request.</p>
57
+request.
5858
59
-<p>The server might be running as an independent server
59
+The server might be running as an independent server
6060
using the <b>server</b> command, or it might be launched from
6161
inetd or xinetd using the <b>http</b> command. Or the server might
6262
be launched from CGI.
6363
(See "[./server/|How To Configure A Fossil Server]" for details.)
6464
The specifics of how the server listens
6565
for incoming HTTP requests is immaterial to this protocol.
6666
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.
6868
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.
7070
The client maintains state between all requests. But on the server
7171
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.
7373
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"
7575
to represent the listener and initiator of the interaction, respectively.
7676
Nothing in this protocol requires that the server actually be a back-room
7777
processor housed in a datacenter, nor does the client need to be a desktop
7878
or handheld device. For the purposes of this article "client" simply means
7979
the repository that initiates the conversation and "server" is the repository
80
-that responds. Nothing more.</p>
80
+that responds. Nothing more.
8181
8282
<h4>2.0.1 HTTPS Transport</h4>
8383
84
-<p>In the current implementation of Fossil, the server only
84
+In the current implementation of Fossil, the server only
8585
understands HTTP requests. The client can send either
8686
clear-text HTTP requests or encrypted HTTPS requests. But when
8787
HTTPS requests are sent, they first must be decrypted by a web server
8888
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.
9090
9191
<h4>2.0.2 SSH Transport</h4>
9292
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
9494
is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh]
9595
to start an instance of the [/help?cmd=test-http|fossil test-http] command
9696
running on the remote machine. It then sends HTTP requests and gets back HTTP
9797
replies over the SSH connection, rather than sending and receiving over an
9898
internet socket. To see the specific "ssh" command that the Fossil client
@@ -99,11 +99,11 @@
9999
runs in order to set up a connection, add either of the the "--httptrace" or
100100
"--sshtrace" options to the "fossil sync" command line.
101101
102102
<h4>2.0.3 FILE Transport</h4>
103103
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
105105
still used. But instead of sending each HTTP request over a socket or
106106
via SSH, the HTTP request is written into a temporary file. The client
107107
then invokes the [/help?cmd=http|fossil http] command in a subprocess
108108
to process the request and and generate a reply. The client then reads
109109
the HTTP reply out of a temporary file on disk, and deletes the two
@@ -111,36 +111,36 @@
111111
in order to implement the "file:" transport, add the "--httptrace"
112112
option to the "fossil sync" command.
113113
114114
<h3>2.1 Server Identification</h3>
115115
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
117117
push, pull, or sync command on the client. (As a convenience to
118118
users, the URL can be omitted on the client command and the same URL
119119
from the most recent push, pull, or sync will be reused. This saves
120120
typing in the common case where the client does multiple syncs to
121
-the same server.)</p>
121
+the same server.)
122122
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>"
124124
to the end. For example, if the URL specified on the client command
125
-line is</p>
125
+line is
126126
127127
<blockquote>
128128
https://fossil-scm.org/fossil
129129
</blockquote>
130130
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:
133133
134134
<blockquote>
135135
https://fossil-scm.org/fossil/xfer
136136
</blockquote>
137137
138138
<h3>2.2 HTTP Request Format</h3>
139139
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:
142142
143143
<blockquote><pre>
144144
POST /fossil/xfer HTTP/1.0
145145
Host: fossil-scm.hwaci.com:80
146146
Content-Type: application/x-fossil
@@ -147,19 +147,19 @@
147147
Content-Length: 4216
148148
149149
<i>content...</i>
150150
</pre></blockquote>
151151
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
153153
on the first line is a copy of the URL pathname. The Host: parameter
154154
is also taken from the URL. The content type is always either
155155
"application/x-fossil" or "application/x-fossil-debug". The "x-fossil"
156156
content type is the default. The only difference is that "x-fossil"
157157
content is compressed using zlib whereas "x-fossil-debug" is sent
158
-uncompressed.</p>
158
+uncompressed.
159159
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:
161161
162162
<blockquote><pre>
163163
HTTP/1.0 200 OK
164164
Date: Mon, 10 Sep 2007 12:21:01 GMT
165165
Connection: close
@@ -168,160 +168,160 @@
168168
Content-Length: 265
169169
170170
<i>content...</i>
171171
</pre></blockquote>
172172
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.
175175
176176
<h2>3.0 Fossil Synchronization Content</h2>
177177
178
-<p>A synchronization request between a client and server consists of
178
+A synchronization request between a client and server consists of
179179
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.
181181
182182
<h3>3.1 Line-oriented Format</h3>
183183
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
185185
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.
187187
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.
189189
The first token on each card is the operator. Subsequent tokens
190190
are arguments. The set of operators understood by servers is slightly
191191
different from the operators understood by clients, though the two
192
-are very similar.</p>
192
+are very similar.
193193
194194
<h3>3.2 Login Cards</h3>
195195
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:
198198
199199
<blockquote>
200200
<b>login</b> <i>userid nonce signature</i>
201201
</blockquote>
202202
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
204204
from the server. The nonce is the SHA1 hash of the remainder of
205205
the message - all text that follows the newline character that
206206
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.
208208
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
210210
that the nonce matches the SHA1 hash of the remainder of the
211211
message. It then checks the signature hash to make sure the
212212
signature matches. If everything
213213
checks out, then the client is granted all privileges of the
214
-specified user.</p>
214
+specified user.
215215
216
-<p>Privileges are cumulative. There can be multiple successful
216
+Privileges are cumulative. There can be multiple successful
217217
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.
219219
220220
<h3>3.3 File Cards</h3>
221221
222
-<p>Artifacts are transferred using either "file" cards, or "cfile"
222
+Artifacts are transferred using either "file" cards, or "cfile"
223223
or "uvfile" cards.
224224
The name "file" card comes from the fact that most artifacts correspond to
225225
files that are under version control.
226226
The "cfile" name is an abbreviation for "compressed file".
227227
The "uvfile" name is an abbreviation for "unversioned file".
228
-</p>
228
+
229229
230230
<h4>3.3.1 Ordinary File Cards</h4>
231231
232
-<p>For sync protocols, artifacts are transferred using "file"
232
+For sync protocols, artifacts are transferred using "file"
233233
cards. File cards come in two different formats depending
234234
on whether the artifact is sent directly or as a delta from some
235
-other artifact.</p>
235
+other artifact.
236236
237237
<blockquote>
238238
<b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
239239
<b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
240240
</blockquote>
241241
242
-<p>File cards are followed by in-line "payload" data.
242
+File cards are followed by in-line "payload" data.
243243
The content of the artifact
244244
or the artifact delta is the first <i>size</i> bytes of the
245245
x-fossil content that immediately follow the newline that
246246
terminates the file card.
247
-</p>
248247
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
250250
is being transferred. The artifact ID is the lower-case hexadecimal
251251
representation of the name hash for the artifact.
252252
The last argument of the file card is the number of bytes of
253253
payload that immediately follow the file card. If the file
254254
card has only two arguments, that means the payload is the
255255
complete content of the artifact. If the file card has three
256256
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.
258258
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
260260
server to client. A delta might be sent before the source of
261261
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.
263263
264264
<h4>3.3.2 Compressed File Cards</h4>
265265
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
267267
receive artifacts as "cfile" cards while cloning. This card was
268268
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.
270270
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
272272
in-line "payload" data characteristics and also the same treatment of
273273
direct content or delta content. Cfile cards come in two different formats
274274
depending on whether the artifact is sent directly or as a delta from
275
-some other artifact.</p>
275
+some other artifact.
276276
277277
<blockquote>
278278
<b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
279279
<b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
280280
</blockquote>
281281
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
283283
is being transferred. The artifact ID is the lower-case hexadecimal
284284
representation of the name hash for the artifact. The second argument of
285285
the cfile card is the original size in bytes of the artifact. The last
286286
argument of the cfile card is the number of compressed bytes of payload
287287
that immediately follow the cfile card. If the cfile card has only
288288
three arguments, that means the payload is the complete content of the
289289
artifact. If the cfile card has four arguments, then the payload is a
290290
delta and the second argument is the ID of another artifact that is the
291291
source of the delta and the third argument is the original size of the
292
-delta artifact.</p>
292
+delta artifact.
293293
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.
296296
297297
<h4>3.3.3 Private artifacts</h4>
298298
299
-<p>"Private" content consist of artifacts that are not normally synced.
299
+"Private" content consist of artifacts that are not normally synced.
300300
However, private content will be synced when the
301301
the [/help?cmd=sync|fossil sync] command includes the "--private" option.
302
-</p>
303302
304
-<p>Private content is marked by a "private" card:
303
+
304
+Private content is marked by a "private" card:
305305
306306
<blockquote>
307307
<b>private</b>
308308
</blockquote>
309309
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.
312312
313313
<h4>3.3.4 Unversioned File Cards</h4>
314314
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
316316
server to client) using "uvfile" cards in the following format:
317317
318318
<blockquote>
319319
<b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
320320
</blockquote>
321321
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
323323
<i>mtime</i> is the last modification time of the file in seconds
324324
since 1970. The <i>hash</i> field is the hash of the content
325325
for the unversioned file, or "<b>-</b>" for deleted content.
326326
The <i>size</i> field is the (uncompressed) size of the content
327327
in bytes. The <i>flags</i> field is an integer which is interpreted
@@ -329,234 +329,234 @@
329329
the <i>content</i> is to be omitted. The content might be omitted if
330330
it is too large to transmit, or if the sender merely wants to update the
331331
modification time of the file without changing the files content.
332332
The <i>content</i> is the (uncompressed) content of the file.
333333
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
335335
size match the content and if the mtime is newer than any existing
336336
instance of the same file held by the receiver. The sender will not
337337
normally transmit a uvfile card unless all these constraints are true,
338338
but the receiver should double-check.
339339
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
341341
the "y" write-unversioned permission.
342342
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
344344
the client. Clients send uvfile cards when they determine that the server
345345
needs the content based on uvigot cards previously received from the server.
346346
347347
<h3>3.4 Push and Pull Cards</h3>
348348
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
350350
the push and pull cards. The push card tells the server that
351351
the client is pushing content. The pull card tells the server
352352
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:
354354
355355
<blockquote>
356356
<b>push</b> <i>servercode projectcode</i><br>
357357
<b>pull</b> <i>servercode projectcode</i>
358358
</blockquote>
359359
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
361361
client. The <i>projectcode</i> is the identifier
362362
of the software project that the client repository contains.
363363
The projectcode for the client and server must match in order
364
-for the transaction to proceed.</p>
364
+for the transaction to proceed.
365365
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
367367
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.
369369
370
-<p>The <i>servercode</i> argument is currently unused.
370
+The <i>servercode</i> argument is currently unused.
371371
372372
<h3>3.5 Clone Cards</h3>
373373
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
375375
client to server in order to tell the server that the client
376376
wants to pull content. The clone card comes in two formats. Older
377377
clients use the no-argument format and newer clients use the
378
-two-argument format.</p>
378
+two-argument format.
379379
380380
<blockquote>
381381
<b>clone</b><br>
382382
<b>clone</b> <i>protocol-version sequence-number</i>
383383
</blockquote>
384384
385385
<h4>3.5.1 Protocol 3</h4>
386386
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
388388
protocol version of "3". (Future versions of Fossil might use larger
389389
protocol version numbers.) Version "3" of the protocol enhanced version
390390
"2" by introducing the "cfile" card which is intended to speed up clone
391391
operations. Instead of sending "file" cards, the server will send "cfile"
392
-cards</p>
392
+cards
393393
394394
<h4>3.5.2 Protocol 2</h4>
395395
396
-<p>The sequence-number sent is the number
396
+The sequence-number sent is the number
397397
of artifacts received so far. For the first clone message, the
398398
sequence number is 0. The server will respond by sending file
399399
cards for some number of artifacts up to the maximum message size.
400400
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
402402
so that the client can know where the server left off.
403403
404404
<blockquote>
405405
<b>clone_seqno</b> <i>sequence-number</i>
406406
</blockquote>
407407
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
409409
operation will use the sequence-number from the
410
-clone_seqno of the previous reply.</p>
410
+clone_seqno of the previous reply.
411411
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
413413
a push message so that the client can discover the projectcode for
414
-this project.</p>
414
+this project.
415415
416416
<h4>3.5.3 Legacy Protocol</h4>
417417
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
419419
to a blank clone card by sending an "igot" card for every artifact in the
420420
repository. The client will then issue "gimme" cards to pull down all the
421421
content it needs.
422422
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
424424
artifacts) but is too slow and unwieldy for larger repositories.
425425
The version 2 protocol is an effort to improve performance. Further
426426
performance improvements with higher-numbered clone protocols are
427427
possible in future versions of Fossil.
428428
429429
<h3>3.6 Igot Cards</h3>
430430
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
432432
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:
434434
435435
<blockquote>
436436
<b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
437437
</blockquote>
438438
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
440440
the sender possesses.
441441
The receiver of an igot card will typically check to see if
442442
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.
444444
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
446446
identified by the first argument is private on the sender and should
447447
be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
448448
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
450450
"I have".
451451
452452
<h4>3.6.1 Unversioned Igot Cards</h4>
453453
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
455455
synchronizing unversioned content. The format of a uvigot card is
456456
as follows:
457457
458458
<blockquote>
459459
<b>uvigot</b> <i>name mtime hash size</i>
460460
</blockquote>
461461
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.
463463
The <i>mtime</i> is the last modification time of the unversioned file
464464
in seconds since 1970.
465465
The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file
466466
content, or "<b>-</b>" if the file has been deleted.
467467
The <i>size</i> is the uncompressed size of the file in bytes.
468468
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
470470
does not match, it sends uvigot cards for every unversioned file that it
471471
holds. The client will use this information to figure out which
472472
unversioned files need to be synchronized.
473473
The server might also send a uvigot card when it receives a uvgimme card
474474
but its reply message size is already oversized and hence unable to hold
475475
the usual uvfile reply.
476476
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
478478
file needs to be transferred from client to server or from server to client.
479479
If a client-to-server transmission is needed, the client schedules that
480480
transfer to occur on a subsequent HTTP request. If a server-to-client
481481
transfer is needed, then the client sends a "uvgimme" card back to the
482482
server to request the file content.
483483
484484
<h3>3.7 Gimme Cards</h3>
485485
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
487487
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:
489489
490490
<blockquote>
491491
<b>gimme</b> <i>artifact-id</i>
492492
</blockquote>
493493
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
495495
the sender wants. The receiver will typically respond to a
496496
gimme card by sending a file card in its reply or in the next
497
-message.</p>
497
+message.
498498
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
500500
pronounced as if it were a single word "gimme" in some dialects of
501501
English (including the dialect spoken by the original author of Fossil).
502502
503503
<h4>3.7.1 Unversioned Gimme Cards</h4>
504504
505
-<p>Sync synchronizing unversioned content, the client may send "uvgimme"
505
+Sync synchronizing unversioned content, the client may send "uvgimme"
506506
cards to the server. A uvgimme card requests that the server send
507507
unversioned content to the client. The format of a uvgimme card is
508508
as follows:
509509
510510
<blockquote>
511511
<b>uvgimme</b> <i>name</i>
512512
</blockquote>
513513
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
515515
server that the client would like to have. When a server sees a
516516
uvgimme card, it normally responses with a uvfile card, though it might
517517
also send another uvigot card if the HTTP reply is already oversized.
518518
519519
<h3>3.8 Cookie Cards</h3>
520520
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
522522
of state information on a client. The server sends a cookie to the
523523
client. The client sends the same cookie back to the server on
524524
its next request. The cookie card has a single argument which
525
-is its payload.</p>
525
+is its payload.
526526
527527
<blockquote>
528528
<b>cookie</b> <i>payload</i>
529529
</blockquote>
530530
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
532532
its next request. Or the client might send a cookie from a different
533533
server on the next request. So the server must not depend on the
534534
cookie and the server must structure the cookie payload in such
535535
a way that it can tell if the cookie it sees is its own cookie or
536536
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.)
538538
539539
<h3>3.9 Request-Configuration Cards</h3>
540540
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
542542
server in order to request that the server send back "configuration"
543543
data. "Configuration" data is information about users or website
544544
appearance or other administrative details which are not part of the
545545
persistent and versioned state of the project. For example, the "name"
546546
of the project, the default Cascading Style Sheet (CSS) for the web-interface,
547547
and the project logo displayed on the web-interface are all configuration
548548
data elements.
549549
550
-<p>The reqconfig card is normally sent in response to the
550
+The reqconfig card is normally sent in response to the
551551
"fossil configuration pull" command. The format is as follows:
552552
553553
<blockquote>
554554
<b>reqconfig</b> <i>configuration-name</i>
555555
</blockquote>
556556
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
558558
following values:
559559
560560
<table border=0 align="center">
561561
<tr><td valign="top">
562562
<ul>
@@ -622,84 +622,81 @@
622622
<li> @concealed
623623
<li> @shun
624624
</ul></td></tr>
625625
</table>
626626
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
628628
Fossil. If the server receives a configuration-name that it does not
629629
understand, the entire reqconfig card is silently ignored. The reqconfig
630630
card might also be ignored if the user lacks sufficient privilege to
631631
access the requested information.
632632
633
-<p>The configuration-names that begin with an alphabetic character refer
633
+The configuration-names that begin with an alphabetic character refer
634634
to values in the "config" table of the server database. For example,
635635
the "logo-image" configuration item refers to the project logo image
636636
that is configured on the Admin page of the [./webui.wiki | web-interface].
637637
The value of the configuration item is returned to the client using a
638638
"config" card.
639639
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
641641
values instead of a single value. The content of these configuration items
642642
is returned in a "config" card that contains pure SQL text that is
643643
intended to be evaluated by the client.
644644
645
-<p>The @user and @concealed configuration items contain sensitive information
645
+The @user and @concealed configuration items contain sensitive information
646646
and are ignored for clients without sufficient privilege.
647647
648648
<h3>3.10 Configuration Cards</h3>
649649
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
651651
to server (in response to a "fossil configuration push" command) or
652652
from server to client (in response to a "fossil configuration pull" or
653653
"fossil clone" command). The format is as follows:
654654
655655
<blockquote>
656656
<b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i>
657657
</blockquote>
658658
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
660660
"Admin" privilege. A client will only accept a config card if
661661
it had sent a corresponding reqconfig card in its request.
662662
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
664664
corresponding configuration data in the receiver.
665665
666666
<h3>3.11 Pragma Cards</h3>
667667
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
669669
issuing a pragma card:
670670
671671
<blockquote>
672672
<b>pragma</i> <i>name value...</i>
673673
</blockquote>
674674
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.
676676
The pragma name defines what the pragma does.
677677
A pragma might have zero or more "value" arguments
678678
depending on the pragma name.
679679
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
681681
in order to enhance the capabilities of Fossil.
682682
Unknown pragmas are silently ignored, for backwards compatibility.
683683
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:
685685
686686
<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
689688
private artifacts to the client. The server will only obey this
690689
request if the user has the "x" or "Private" privilege.
691690
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
694692
cards for every known artifact. This can help the client and server
695693
to get back in synchronization after a prior protocol error. The
696694
"--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.
698696
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
701698
synchronization of unversioned content. The <i>HASH</i> is a SHA1
702699
hash of the names, modification times, and individual hashes of all
703700
unversioned files on the client. If the unversioned content hash
704701
from the client does not match the unversioned content hash on the
705702
server, then the server will reply with either a "pragma uv-push-ok"
@@ -707,157 +704,151 @@
707704
each unversioned file currently held on the server. The collection
708705
of "uvigot" cards sent in response to a "uv-hash" pragma is called
709706
the "unversioned catalog". The client will used the unversioned
710707
catalog to figure out which files (if any) need to be synchronized
711708
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.
713710
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
715712
either a uv-pull-only or uv-push-ok pragma, that means that the
716713
content on the server exactly matches the content on the client and
717714
no further synchronization is required.
718715
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
721717
to a uv-hash pragma with a mismatched content hash argument. This
722718
pragma indicates that there are differences in unversioned content
723719
between the client and server but that content can only be transferred
724720
from server to client. The server is unwilling to accept content from
725721
the client because the client login lacks the "write-unversioned"
726
-permission.</p>
722
+permission.
727723
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
730725
to a uv-hash pragma with a mismatched content hash argument. This
731726
pragma indicates that there are differences in unversioned content
732727
between the client and server and that content can be transferred
733728
in either direction. The server is willing to accept content from
734729
the client because the client login has the "write-unversioned"
735
-permission.</p>
730
+permission.
736731
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
739733
that it is about to add a new check-in as a child of the
740734
CHECKIN-HASH check-in and on the same branch as CHECKIN-HASH.
741735
If some other client has already indicated that it was also
742736
trying to commit against CHECKIN-HASH, that indicates that a
743737
fork is about to occur, and the server will reply with
744738
a "ci-lock-fail" pragma (see below). Check-in locks
745739
automatically expire when the check-in actually occurs, or
746740
after a timeout (currently one minute but subject to change).
747741
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
750743
for the same check-in but from different clients, the second a
751744
subsequent ci-lock will provoke a ci-lock-fail pragma in the
752745
reply to let the client know that it if continues with the
753746
check-in it will likely generate a fork. The LOGIN and MTIME
754747
arguments are intended to provide information to the client to
755748
help it generate a more useful error message.
756
-</p>
757749
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
760751
a successful commit. This instructs the server to release
761752
any lock on any check-in previously held by that client.
762753
The ci-unlock pragma helps to avoid false-positive lock warnings
763754
that might arise if a check-in is aborted and then restarted
764755
on a branch.
765756
</ol>
766757
767758
<h3>3.12 Comment Cards</h3>
768759
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.
771762
772763
<h3>3.13 Message and Error Cards</h3>
773764
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
775766
an error card in its reply. When the client sees the error card,
776767
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:
778769
779770
<blockquote>
780771
<b>error</b> <i>error-message</i>
781772
</blockquote>
782773
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
784775
be a single token.
785776
A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A
786777
newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash
787778
(ASCII 0x5C) is represented as two backslashes "\\". Apart from
788779
space and newline, no other whitespace characters nor any
789780
unprintable characters are allowed in
790
-the error message.</p>
781
+the error message.
791782
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
793784
message on the client console, but which is not an error:
794785
795786
<blockquote>
796787
<b>message</b> <i>message-text</i>
797788
</blockquote>
798789
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.
800791
801792
<h3>3.14 Unknown Cards</h3>
802793
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.
805796
806797
<h2>4.0 Phantoms And Clusters</h2>
807798
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
809800
that artifact, but it does not know the artifact content, then it stores that
810801
artifact as a "phantom". A repository will typically create a phantom when
811802
it receives an igot card for an artifact that it does not hold or when it
812803
receives a file card that references a delta source that it does not
813804
hold. When a server is generating its reply or when a client is
814805
generating a new request, it will usually send gimme cards for every
815
-phantom that it holds.</p>
806
+phantom that it holds.
816807
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
818809
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.
820811
821
-<p>A cluster is line oriented. Each line of a cluster
812
+A cluster is line oriented. Each line of a cluster
822813
is a card. The cards are separated by the newline ("\n") character.
823814
Each card consists of a single character card type, a space, and a
824815
single argument. No extra whitespace and no trailing or leading
825816
whitespace is allowed. All cards in the cluster must occur in
826
-strict lexicographical order.</p>
817
+strict lexicographical order.
827818
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
829820
"Z" card. Each M card holds an argument which is an artifact ID for an
830821
artifact in the repository. The Z card has a single argument which is the
831822
lower-case hexadecimal representation of the MD5 checksum of all
832823
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.
834825
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
836827
exactly is not a cluster. There must be no extra whitespace in
837828
the artifact. There must be one or more M cards. There must be a
838829
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.
840831
841832
<h3>4.1 The Unclustered Table</h3>
842833
843
-<p>Every repository maintains a table named "<b>unclustered</b>"
834
+Every repository maintains a table named "<b>unclustered</b>"
844835
which records the identity of every artifact and phantom it holds that is not
845836
mentioned in a cluster. The entries in the unclustered table can
846837
be thought of as leaves on a tree of artifacts. Some of the unclustered
847838
artifacts will be other clusters. Those clusters may contain other clusters,
848839
which might contain still more clusters, and so forth. Beginning
849840
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.
851842
852843
<h2>5.0 Synchronization Strategies</h2>
853844
854845
<h3>5.1 Pull</h3>
855846
856
-<p>A typical pull operation proceeds as shown below. Details
847
+A typical pull operation proceeds as shown below. Details
857848
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:
859850
860851
<ol>
861852
<li>The client sends login and pull cards.
862853
<li>The client sends a cookie card if it has previously received a cookie.
863854
<li>The client sends gimme cards for every phantom that it holds.
@@ -877,38 +868,38 @@
877868
that mentions an artifact that the client does not possess.
878869
<li>The client creates a phantom for the delta source of file cards when
879870
the delta source is an artifact that the client does not possess.
880871
</ol>
881872
882
-<p>These ten steps represent a single HTTP round-trip request.
873
+These ten steps represent a single HTTP round-trip request.
883874
The first three steps are the processing that occurs on the client
884875
to generate the request. The middle four steps are processing
885876
that occurs on the server to interpret the request and generate a
886877
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.
888879
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.
891882
892
-<p>Note that the server tries
883
+Note that the server tries
893884
to limit the size of its reply message to something reasonable
894885
(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.
896887
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.
898889
By only creating clusters on the server, we hope to minimize the
899890
amount of overlap between clusters in the common configuration where
900891
there is a single server and many clients. The same synchronization
901892
protocol will continue to work even if there are multiple servers
902893
or if servers and clients sometimes change roles. The only negative
903894
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.
905896
906897
<h3>5.2 Push</h3>
907898
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.
910901
911902
<ol>
912903
<li>The client sends login and push cards.
913904
<li>The client sends file cards for any artifacts that it holds that have
914905
never before been pushed - artifacts that come from local check-ins.
@@ -929,40 +920,40 @@
929920
<hr>
930921
<li>The client remembers the gimme cards from the server so that it
931922
can generate file cards in reply on the next cycle.
932923
</ol>
933924
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
935926
server knows all artifacts that exist on the client. Also, as with
936927
pull, the client attempts to keep the size of the request from
937928
growing too large by suppressing file cards once the
938
-size of the request reaches 1MB.</p>
929
+size of the request reaches 1MB.
939930
940931
<h3 id="sync">5.3 Sync</h3>
941932
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.
943934
The first three steps of a pull are combined with the first five steps
944935
of a push. Steps (4) through (7) of a pull are combined with steps
945936
(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.
947938
948939
<h3>5.4 Unversioned File Sync</h3>
949940
950
-<p>"Unversioned files" are files held in the repository
941
+"Unversioned files" are files held in the repository
951942
where only the most recent version of the file is kept rather than
952943
the entire change history. Unversioned files are intended to be
953944
used to store ephemeral content, such as compiled binaries of the
954945
most recent release.
955946
956
-<p>Unversioned files are identified by name and timestamp (mtime).
947
+Unversioned files are identified by name and timestamp (mtime).
957948
Only the most recent version of each file (the version with
958949
the largest mtime value) is retained.
959950
960
-<p>Unversioned files are synchronized using the
951
+Unversioned files are synchronized using the
961952
[/help?cmd=unversioned|fossil unversioned sync] command.
962953
963
-<p>A schematic of an unversioned file synchronization is as follows:
954
+A schematic of an unversioned file synchronization is as follows:
964955
965956
<ol>
966957
<li>The client sends a "pragma uv-hash" card to the server. The argument
967958
to the uv-hash pragma is a hash of all filesnames, mtimes, and
968959
content hashes for the unversioned files held by the client.
@@ -981,17 +972,17 @@
981972
<hr>
982973
<li>The server updates its unversioned file store with received "uvfile"
983974
cards and answers "uvgimme" cards with "uvfile" cards in its reply.
984975
</ol>
985976
986
-<p>The last two steps might be repeated multiple
977
+The last two steps might be repeated multiple
987978
times if there is more unversioned content to be transferred than will
988979
fit comfortably in a single HTTP request.
989980
990981
<h2>6.0 Summary</h2>
991982
992
-<p>Here are the key points of the synchronization protocol:</p>
983
+Here are the key points of the synchronization protocol:
993984
994985
<ol>
995986
<li>The client sends one or more PUSH HTTP requests to the server.
996987
The request and reply content type is "application/x-fossil".
997988
<li>HTTP request content is compressed using zlib.
@@ -1031,11 +1022,11 @@
10311022
gimme messages for those artifacts.
10321023
</ol>
10331024
10341025
<h2>7.0 Troubleshooting And Debugging Hints</h2>
10351026
1036
-<p>
1027
+
10371028
If you run the [/help?cmd=sync|fossil sync] command
10381029
(or [/help?cmd=pull|pull] or [/help?cmd=push|push] or
10391030
[/help?cmd=clone|clone]) with the --httptrace option, Fossil
10401031
will keep a copy of each HTTP request and reply in files
10411032
named:
@@ -1042,17 +1033,17 @@
10421033
<ul>
10431034
<li> <tt>http-request-</tt><i>N</i><tt>.txt</tt>
10441035
<li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt>
10451036
</ul>
10461037
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
10481039
round-trip. If you are having trouble on the server side,
10491040
you can run the "[/help?cmd=test-http|fossil test-http]" command in a
10501041
debugger using one the "http-request-N.txt" files as input and
10511042
single step through the processing performed by the server.
10521043
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]
10541045
(and similar) causes the external program "CMD" to be used to move
10551046
the sync message to the server and retrieve the sync reply. The
10561047
CMD is given three arguments:
10571048
<ol>
10581049
<li> The URL of the server
@@ -1060,9 +1051,9 @@
10601051
protocol text, with the HTTP headers
10611052
<li> The name of a temporary file into which the CMD should write the
10621053
reply sync protocol text, again without any HTTP headers
10631054
</ol>
10641055
1065
-<p>In a complex debugging situation, you can run the command
1056
+In a complex debugging situation, you can run the command
10661057
"fossil sync --transport-command ./debugging_script" where
10671058
"debugging_script" is some script of your own that invokes
10681059
the anomolous behavior your are trying to debug.
10691060
--- 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 @@
11
<title>Unversioned Content</title>
22
<h1 align="center">Unversioned Content</h1>
33
44
"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.
77
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.
1112
1213
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
1617
content, for example.
1718
1819
<h2>Accessing Unversioned Files</h2>
1920
2021
Unversioned files are <u>not</u> a part of a check-out.
2122
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>".
2324
In other words, the URI method "<b>uv</b>" (short for "unversioned")
2425
followed by the name of the unversioned file will retrieve the content
2526
of the file. The MIME type is inferred from the filename suffix.
2627
2728
The content of unversioned files can also be retrieved using the
@@ -30,36 +31,43 @@
3031
A list of all unversioned files on a server can be seen using
3132
the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]).
3233
3334
<h2>Syncing Unversioned Files</h2>
3435
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:
3838
3939
<blockquote><pre>
4040
fossil sync <b>-u</b>
4141
fossil clone <b>-u</b> <i>URL local-repo-name</i>
4242
fossil unversioned sync
4343
</pre></blockquote>
4444
4545
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
4949
unversioned content without synchronizing anything else.
5050
5151
Notice that the "-u" option does not work on
5252
[/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull].
5353
The "-u" option is only available on "sync" and "clone".
54
-
5554
A rough equivalent of an unversioned pull would be the
5655
[/help?cmd=unversioned|fossil unversioned revert] command. The
5756
"unversioned revert"
5857
command causes the unversioned content on the local repository to overwritten
5958
by the unversioned content found on the remote repository.
6059
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
+
6169
<h2>Implementation Details</h2>
6270
6371
<i>(This section outlines the current implementation of unversioned
6472
files. This is not an interface spec and hence subject to change.)</i>
6573
@@ -77,23 +85,30 @@
7785
encoding INT, -- 0: plaintext 1: zlib compressed
7886
content BLOB -- File content
7987
);
8088
</pre></blockquote>
8189
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
8594
is to run:
8695
8796
<blockquote><pre>
8897
fossil sql "DROP TABLE unversioned; VACUUM;"
8998
</pre></blockquote>
9099
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
99114
information.
100115
--- 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
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -4,11 +4,12 @@
44
Fossil uses [/wiki_rules | Fossil wiki markup] and/or
55
[/md_rules | Markdown markup] for many things:
66
77
* Stand-alone wiki pages.
88
* 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.)
1011
* [./embeddeddoc.wiki | Embedded documentation] files whose
1112
name ends in ".wiki" or ".md" or ".markdown".
1213
* [./event.wiki | Technical notes].
1314
* [./forum.wiki | Forum messages].
1415
* Auxiliary notes on check-ins and branches.
@@ -60,14 +61,18 @@
6061
format is used, it is trivial to move a file from wiki to embedded documentation
6162
or back again as the project evolves.
6263
6364
<h2>Bug-reports and check-in comments and Forum messages</h2>
6465
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.
6974
7075
<h2 id="assocwiki">Auxiliary notes attached to check-ins or branches</h2>
7176
7277
Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>"
7378
or "checkin/<i>HASH</i>" are associated with the corresponding
7479
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button