Fossil SCM

merge trunk

jan.nijtmans 2015-02-05 09:10 svn-import merge
Commit 9e7ea6a20fd81a1487ba5fc16902e09ff04c535c
+1 -1
--- auto.def
+++ auto.def
@@ -68,11 +68,11 @@
6868
6969
find_internal_sqlite
7070
}
7171
7272
if {[string match *-solaris* [get-define host]]} {
73
- define-append EXTRA_CFLAGS -D_XOPEN_SOURCE=500
73
+ define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
7474
}
7575
7676
if {[opt-bool fossil-debug]} {
7777
define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
7878
msg-result "Debugging support enabled"
7979
--- auto.def
+++ auto.def
@@ -68,11 +68,11 @@
68
69 find_internal_sqlite
70 }
71
72 if {[string match *-solaris* [get-define host]]} {
73 define-append EXTRA_CFLAGS -D_XOPEN_SOURCE=500
74 }
75
76 if {[opt-bool fossil-debug]} {
77 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
78 msg-result "Debugging support enabled"
79
--- auto.def
+++ auto.def
@@ -68,11 +68,11 @@
68
69 find_internal_sqlite
70 }
71
72 if {[string match *-solaris* [get-define host]]} {
73 define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__}
74 }
75
76 if {[opt-bool fossil-debug]} {
77 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
78 msg-result "Debugging support enabled"
79
+224 -121
--- autosetup/jimsh0.c
+++ autosetup/jimsh0.c
@@ -38,15 +38,17 @@
3838
#define TCL_PLATFORM_PLATFORM "unix"
3939
#define TCL_PLATFORM_PATH_SEPARATOR ":"
4040
#define HAVE_VFORK
4141
#define HAVE_WAITPID
4242
#define HAVE_ISATTY
43
+#define HAVE_MKSTEMP
44
+#define HAVE_LINK
4345
#define HAVE_SYS_TIME_H
4446
#define HAVE_DIRENT_H
4547
#define HAVE_UNISTD_H
4648
#endif
47
-#define JIM_VERSION 75
49
+#define JIM_VERSION 76
4850
#ifndef JIM_WIN32COMPAT_H
4951
#define JIM_WIN32COMPAT_H
5052
5153
5254
@@ -113,10 +115,11 @@
113115
int closedir(DIR *dir);
114116
struct dirent *readdir(DIR *dir);
115117
116118
#elif defined(__MINGW32__)
117119
120
+#include <stdlib.h>
118121
#define strtod __strtod
119122
120123
#endif
121124
122125
#endif
@@ -1048,29 +1051,48 @@
10481051
"\n"
10491052
"\n"
10501053
"\n"
10511054
"proc _jimsh_init {} {\n"
10521055
" rename _jimsh_init {}\n"
1056
+" global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
10531057
"\n"
10541058
"\n"
1055
-" lappend p {*}[split [env JIMLIB {}] $::tcl_platform(pathSeparator)]\n"
1056
-" lappend p {*}$::auto_path\n"
1057
-" lappend p [file dirname [info nameofexecutable]]\n"
1058
-" set ::auto_path $p\n"
1059
+" if {[exists jim::argv0]} {\n"
1060
+" if {[string match \"*/*\" $jim::argv0]} {\n"
1061
+" set jim::exe [file join [pwd] $jim::argv0]\n"
1062
+" } else {\n"
1063
+" foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1064
+" set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1065
+" if {[file executable $exec]} {\n"
1066
+" set jim::exe $exec\n"
1067
+" break\n"
1068
+" }\n"
1069
+" }\n"
1070
+" }\n"
1071
+" }\n"
10591072
"\n"
1060
-" if {$::tcl_interactive && [env HOME {}] ne \"\"} {\n"
1073
+"\n"
1074
+" lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1075
+" if {[exists jim::exe]} {\n"
1076
+" lappend p [file dirname $jim::exe]\n"
1077
+" }\n"
1078
+" lappend p {*}$auto_path\n"
1079
+" set auto_path $p\n"
1080
+"\n"
1081
+" if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
10611082
" foreach src {.jimrc jimrc.tcl} {\n"
10621083
" if {[file exists [env HOME]/$src]} {\n"
10631084
" uplevel #0 source [env HOME]/$src\n"
10641085
" break\n"
10651086
" }\n"
10661087
" }\n"
10671088
" }\n"
1089
+" return \"\"\n"
10681090
"}\n"
10691091
"\n"
10701092
"if {$tcl_platform(platform) eq \"windows\"} {\n"
1071
-" set jim_argv0 [string map {\\\\ /} $jim_argv0]\n"
1093
+" set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
10721094
"}\n"
10731095
"\n"
10741096
"_jimsh_init\n"
10751097
);
10761098
}
@@ -1091,11 +1113,11 @@
10911113
"\n"
10921114
"\n"
10931115
"proc glob.globdir {dir pattern} {\n"
10941116
" if {[file exists $dir/$pattern]} {\n"
10951117
"\n"
1096
-" return $pattern\n"
1118
+" return [list $pattern]\n"
10971119
" }\n"
10981120
"\n"
10991121
" set result {}\n"
11001122
" set files [readdir $dir]\n"
11011123
" lappend files . ..\n"
@@ -1153,11 +1175,11 @@
11531175
" }\n"
11541176
"\n"
11551177
" foreach old $oldexp {\n"
11561178
" lappend newexp $old$suf\n"
11571179
" }\n"
1158
-" linsert $newexp 0 $rest\n"
1180
+" list $rest {*}$newexp\n"
11591181
"}\n"
11601182
"\n"
11611183
"\n"
11621184
"\n"
11631185
"proc glob.glob {base pattern} {\n"
@@ -1200,10 +1222,11 @@
12001222
"\n"
12011223
"\n"
12021224
"proc glob {args} {\n"
12031225
" set nocomplain 0\n"
12041226
" set base \"\"\n"
1227
+" set tails 0\n"
12051228
"\n"
12061229
" set n 0\n"
12071230
" foreach arg $args {\n"
12081231
" if {[info exists param]} {\n"
12091232
" set $param $arg\n"
@@ -1217,21 +1240,20 @@
12171240
" set param base\n"
12181241
" }\n"
12191242
" -n* {\n"
12201243
" set nocomplain 1\n"
12211244
" }\n"
1222
-" -t* {\n"
1223
-"\n"
1224
-" }\n"
1225
-"\n"
1226
-" -* {\n"
1227
-" return -code error \"bad option \\\"$switch\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1245
+" -ta* {\n"
1246
+" set tails 1\n"
12281247
" }\n"
12291248
" -- {\n"
12301249
" incr n\n"
12311250
" break\n"
12321251
" }\n"
1252
+" -* {\n"
1253
+" return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1254
+" }\n"
12331255
" * {\n"
12341256
" break\n"
12351257
" }\n"
12361258
" }\n"
12371259
" incr n\n"
@@ -1245,29 +1267,35 @@
12451267
"\n"
12461268
" set args [lrange $args $n end]\n"
12471269
"\n"
12481270
" set result {}\n"
12491271
" foreach pattern $args {\n"
1250
-" set pattern [string map {\n"
1272
+" set escpattern [string map {\n"
12511273
" \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
12521274
" } $pattern]\n"
1253
-" set patexps [lassign [glob.explode $pattern] rest]\n"
1275
+" set patexps [lassign [glob.explode $escpattern] rest]\n"
12541276
" if {$rest ne \"\"} {\n"
12551277
" return -code error \"unmatched close brace in glob pattern\"\n"
12561278
" }\n"
12571279
" foreach patexp $patexps {\n"
12581280
" set patexp [string map {\n"
12591281
" \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
12601282
" } $patexp]\n"
12611283
" foreach {realname name} [glob.glob $base $patexp] {\n"
1262
-" lappend result $name\n"
1284
+" incr n\n"
1285
+" if {$tails} {\n"
1286
+" lappend result $name\n"
1287
+" } else {\n"
1288
+" lappend result [file join $base $name]\n"
1289
+" }\n"
12631290
" }\n"
12641291
" }\n"
12651292
" }\n"
12661293
"\n"
12671294
" if {!$nocomplain && [llength $result] == 0} {\n"
1268
-" return -code error \"no files matched glob patterns\"\n"
1295
+" set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1296
+" return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
12691297
" }\n"
12701298
"\n"
12711299
" return $result\n"
12721300
"}\n"
12731301
);
@@ -1348,11 +1376,11 @@
13481376
"\n"
13491377
" lappend stacktrace {*}[stacktrace 1]\n"
13501378
" }\n"
13511379
" lassign $stacktrace p f l\n"
13521380
" if {$f ne \"\"} {\n"
1353
-" set result \"Runtime Error: $f:$l: \"\n"
1381
+" set result \"$f:$l: Error: \"\n"
13541382
" }\n"
13551383
" append result \"$msg\\n\"\n"
13561384
" append result [stackdump $stacktrace]\n"
13571385
"\n"
13581386
"\n"
@@ -1360,22 +1388,13 @@
13601388
"}\n"
13611389
"\n"
13621390
"\n"
13631391
"\n"
13641392
"proc {info nameofexecutable} {} {\n"
1365
-" if {[info exists ::jim_argv0]} {\n"
1366
-" if {[string match \"*/*\" $::jim_argv0]} {\n"
1367
-" return [file join [pwd] $::jim_argv0]\n"
1368
-" }\n"
1369
-" foreach path [split [env PATH \"\"] $::tcl_platform(pathSeparator)] {\n"
1370
-" set exec [file join [pwd] [string map {\\\\ /} $path] $::jim_argv0]\n"
1371
-" if {[file executable $exec]} {\n"
1372
-" return $exec\n"
1373
-" }\n"
1374
-" }\n"
1393
+" if {[exists ::jim::exe]} {\n"
1394
+" return $::jim::exe\n"
13751395
" }\n"
1376
-" return \"\"\n"
13771396
"}\n"
13781397
"\n"
13791398
"\n"
13801399
"proc {dict with} {&dictVar {args key} script} {\n"
13811400
" set keys {}\n"
@@ -1587,11 +1606,11 @@
15871606
" try {\n"
15881607
" if {$force ni {{} -force}} {\n"
15891608
" error \"bad option \\\"$force\\\": should be -force\"\n"
15901609
" }\n"
15911610
"\n"
1592
-" set in [open $source]\n"
1611
+" set in [open $source rb]\n"
15931612
"\n"
15941613
" if {[file exists $target]} {\n"
15951614
" if {$force eq \"\"} {\n"
15961615
" error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
15971616
" }\n"
@@ -1605,11 +1624,11 @@
16051624
" file stat $target ts\n"
16061625
" if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
16071626
" return\n"
16081627
" }\n"
16091628
" }\n"
1610
-" set out [open $target w]\n"
1629
+" set out [open $target wb]\n"
16111630
" $in copyto $out\n"
16121631
" $out close\n"
16131632
" } on error {msg opts} {\n"
16141633
" incr opts(-level)\n"
16151634
" return {*}$opts $msg\n"
@@ -1691,11 +1710,11 @@
16911710
" }\n"
16921711
" if {[llength $args] == 0} {\n"
16931712
" return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
16941713
" }\n"
16951714
" set args [lassign $args script]\n"
1696
-" set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts]\n"
1715
+" set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
16971716
"\n"
16981717
" set handled 0\n"
16991718
"\n"
17001719
" foreach {on codes vars script} $args {\n"
17011720
" switch -- $on \\\n"
@@ -1709,16 +1728,16 @@
17091728
" if {$optsvar ne \"\"} {\n"
17101729
" upvar $optsvar hopts\n"
17111730
" set hopts $opts\n"
17121731
" }\n"
17131732
"\n"
1714
-" set code [catch [list uplevel 1 $script] msg opts]\n"
1733
+" set code [catch {uplevel 1 $script} msg opts]\n"
17151734
" incr handled\n"
17161735
" }\n"
17171736
" } \\\n"
17181737
" finally {\n"
1719
-" set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]\n"
1738
+" set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
17201739
" if {$finalcode} {\n"
17211740
"\n"
17221741
" set code $finalcode\n"
17231742
" set msg $finalmsg\n"
17241743
" set opts $finalopts\n"
@@ -2502,10 +2521,11 @@
25022521
Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
25032522
25042523
return JIM_OK;
25052524
}
25062525
2526
+#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
25072527
static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
25082528
const char *hdlfmt, int family, const char *mode[2])
25092529
{
25102530
if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) {
25112531
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
@@ -2522,10 +2542,11 @@
25222542
close(p[0]);
25232543
close(p[1]);
25242544
JimAioSetError(interp, NULL);
25252545
return JIM_ERR;
25262546
}
2547
+#endif
25272548
25282549
25292550
int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
25302551
{
25312552
#ifdef HAVE_MKSTEMP
@@ -2552,19 +2573,19 @@
25522573
25532574
25542575
fd = mkstemp(filenameObj->bytes);
25552576
umask(mask);
25562577
if (fd < 0) {
2557
- Jim_SetResultString(interp, "Failed to create tempfile", -1);
2578
+ JimAioSetError(interp, filenameObj);
25582579
Jim_FreeNewObj(interp, filenameObj);
25592580
return -1;
25602581
}
25612582
25622583
Jim_SetResult(interp, filenameObj);
25632584
return fd;
25642585
#else
2565
- Jim_SetResultString(interp, "tempfile not supported", -1);
2586
+ Jim_SetResultString(interp, "platform has no tempfile support", -1);
25662587
return -1;
25672588
#endif
25682589
}
25692590
25702591
FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
@@ -3176,10 +3197,16 @@
31763197
31773198
# ifndef MAXPATHLEN
31783199
# define MAXPATHLEN JIM_PATH_LEN
31793200
# endif
31803201
3202
+#if defined(__MINGW32__) || defined(_MSC_VER)
3203
+#define ISWINDOWS 1
3204
+#else
3205
+#define ISWINDOWS 0
3206
+#endif
3207
+
31813208
31823209
static const char *JimGetFileType(int mode)
31833210
{
31843211
if (S_ISREG(mode)) {
31853212
return "file";
@@ -3281,16 +3308,14 @@
32813308
Jim_SetResultString(interp, ".", -1);
32823309
}
32833310
else if (p == path) {
32843311
Jim_SetResultString(interp, "/", -1);
32853312
}
3286
-#if defined(__MINGW32__) || defined(_MSC_VER)
3287
- else if (p[-1] == ':') {
3313
+ else if (ISWINDOWS && p[-1] == ':') {
32883314
32893315
Jim_SetResultString(interp, path, p - path + 1);
32903316
}
3291
-#endif
32923317
else {
32933318
Jim_SetResultString(interp, path, p - path);
32943319
}
32953320
return JIM_OK;
32963321
}
@@ -3373,16 +3398,14 @@
33733398
33743399
if (*part == '/') {
33753400
33763401
last = newname;
33773402
}
3378
-#if defined(__MINGW32__) || defined(_MSC_VER)
3379
- else if (strchr(part, ':')) {
3403
+ else if (ISWINDOWS && strchr(part, ':')) {
33803404
33813405
last = newname;
33823406
}
3383
-#endif
33843407
else if (part[0] == '.') {
33853408
if (part[1] == '/') {
33863409
part += 2;
33873410
len -= 2;
33883411
}
@@ -3407,11 +3430,14 @@
34073430
last += len;
34083431
}
34093432
34103433
34113434
if (last > newname + 1 && last[-1] == '/') {
3412
- *--last = 0;
3435
+
3436
+ if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3437
+ *--last = 0;
3438
+ }
34133439
}
34143440
}
34153441
34163442
*last = 0;
34173443
@@ -3591,10 +3617,48 @@
35913617
return JIM_ERR;
35923618
}
35933619
35943620
return JIM_OK;
35953621
}
3622
+
3623
+#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3624
+static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3625
+{
3626
+ int ret;
3627
+ const char *source;
3628
+ const char *dest;
3629
+ static const char * const options[] = { "-hard", "-symbolic", NULL };
3630
+ enum { OPT_HARD, OPT_SYMBOLIC, };
3631
+ int option = OPT_HARD;
3632
+
3633
+ if (argc == 3) {
3634
+ if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3635
+ return JIM_ERR;
3636
+ }
3637
+ argv++;
3638
+ argc--;
3639
+ }
3640
+
3641
+ dest = Jim_String(argv[0]);
3642
+ source = Jim_String(argv[1]);
3643
+
3644
+ if (option == OPT_HARD) {
3645
+ ret = link(source, dest);
3646
+ }
3647
+ else {
3648
+ ret = symlink(source, dest);
3649
+ }
3650
+
3651
+ if (ret != 0) {
3652
+ Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3653
+ strerror(errno));
3654
+ return JIM_ERR;
3655
+ }
3656
+
3657
+ return JIM_OK;
3658
+}
3659
+#endif
35963660
35973661
static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
35983662
{
35993663
const char *path = Jim_String(filename);
36003664
@@ -3889,10 +3953,19 @@
38893953
file_cmd_rename,
38903954
2,
38913955
3,
38923956
38933957
},
3958
+#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3959
+ { "link",
3960
+ "?-symbolic|-hard? newname target",
3961
+ file_cmd_link,
3962
+ 2,
3963
+ 3,
3964
+
3965
+ },
3966
+#endif
38943967
#if defined(HAVE_READLINK)
38953968
{ "readlink",
38963969
"name",
38973970
file_cmd_readlink,
38983971
1,
@@ -3982,19 +4055,17 @@
39824055
if (getcwd(cwd, MAXPATHLEN) == NULL) {
39834056
Jim_SetResultString(interp, "Failed to get pwd", -1);
39844057
Jim_Free(cwd);
39854058
return JIM_ERR;
39864059
}
3987
-#if defined(__MINGW32__) || defined(_MSC_VER)
3988
- {
4060
+ else if (ISWINDOWS) {
39894061
39904062
char *p = cwd;
39914063
while ((p = strchr(p, '\\')) != NULL) {
39924064
*p++ = '/';
39934065
}
39944066
}
3995
-#endif
39964067
39974068
Jim_SetResultString(interp, cwd, -1);
39984069
39994070
Jim_Free(cwd);
40004071
return JIM_OK;
@@ -5322,22 +5393,20 @@
53225393
53235394
static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
53245395
{
53255396
int fd = Jim_MakeTempFile(interp, NULL);
53265397
5327
- if (fd == JIM_BAD_FD) {
5328
- Jim_SetResultErrno(interp, "couldn't create temp file");
5329
- return -1;
5330
- }
5331
- unlink(Jim_String(Jim_GetResult(interp)));
5332
- if (contents) {
5333
- if (write(fd, contents, len) != len) {
5334
- Jim_SetResultErrno(interp, "couldn't write temp file");
5335
- close(fd);
5336
- return -1;
5337
- }
5338
- lseek(fd, 0L, SEEK_SET);
5398
+ if (fd != JIM_BAD_FD) {
5399
+ unlink(Jim_String(Jim_GetResult(interp)));
5400
+ if (contents) {
5401
+ if (write(fd, contents, len) != len) {
5402
+ Jim_SetResultErrno(interp, "couldn't write temp file");
5403
+ close(fd);
5404
+ return -1;
5405
+ }
5406
+ lseek(fd, 0L, SEEK_SET);
5407
+ }
53395408
}
53405409
return fd;
53415410
}
53425411
53435412
static char **JimSaveEnv(char **env)
@@ -7768,10 +7837,11 @@
77687837
77697838
const char *Jim_String(Jim_Obj *objPtr)
77707839
{
77717840
if (objPtr->bytes == NULL) {
77727841
7842
+ JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
77737843
JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
77747844
objPtr->typePtr->updateStringProc(objPtr);
77757845
}
77767846
return objPtr->bytes;
77777847
}
@@ -8530,11 +8600,11 @@
85308600
return objPtr;
85318601
}
85328602
85338603
static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
85348604
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8535
-static int JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8605
+static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
85368606
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
85378607
85388608
static const Jim_ObjType scriptObjType = {
85398609
"script",
85408610
FreeScriptInternalRep,
@@ -8558,10 +8628,11 @@
85588628
int inUse; /* Used to share a ScriptObj. Currently
85598629
only used by Jim_EvalObj() as protection against
85608630
shimmering of the currently evaluated object. */
85618631
int firstline;
85628632
int linenr;
8633
+ int missing;
85638634
} ScriptObj;
85648635
85658636
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
85668637
{
85678638
int i;
@@ -8836,19 +8907,18 @@
88368907
}
88378908
88388909
script->len = i;
88398910
}
88408911
8841
-static int JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
8912
+static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
88428913
{
88438914
int scriptTextLen;
88448915
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
88458916
struct JimParserCtx parser;
88468917
struct ScriptObj *script;
88478918
ParseTokenList tokenlist;
88488919
int line = 1;
8849
- int retcode = JIM_OK;
88508920
88518921
88528922
if (objPtr->typePtr == &sourceObjType) {
88538923
line = objPtr->internalRep.sourceValue.lineNumber;
88548924
}
@@ -8861,12 +8931,10 @@
88618931
JimParseScript(&parser);
88628932
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
88638933
parser.tline);
88648934
}
88658935
8866
- retcode = JimParseCheckMissing(interp, parser.missing.ch);
8867
-
88688936
88698937
ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
88708938
88718939
88728940
script = Jim_Alloc(sizeof(*script));
@@ -8876,12 +8944,13 @@
88768944
script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
88778945
}
88788946
else {
88798947
script->fileNameObj = interp->emptyObj;
88808948
}
8881
- script->linenr = parser.missing.line;
88828949
Jim_IncrRefCount(script->fileNameObj);
8950
+ script->missing = parser.missing.ch;
8951
+ script->linenr = parser.missing.line;
88838952
88848953
ScriptObjAddTokens(interp, script, &tokenlist);
88858954
88868955
88878956
ScriptTokenListFree(&tokenlist);
@@ -8888,28 +8957,37 @@
88888957
88898958
88908959
Jim_FreeIntRep(interp, objPtr);
88918960
Jim_SetIntRepPtr(objPtr, script);
88928961
objPtr->typePtr = &scriptObjType;
8893
-
8894
- return retcode;
88958962
}
88968963
8897
-ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
8964
+static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
8965
+
8966
+ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
88988967
{
88998968
if (objPtr == interp->emptyObj) {
89008969
89018970
objPtr = interp->nullScriptObj;
89028971
}
89038972
89048973
if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
8905
- if (JimSetScriptFromAny(interp, objPtr) == JIM_ERR) {
8906
- return NULL;
8907
- }
8974
+ JimSetScriptFromAny(interp, objPtr);
89088975
}
8909
- return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
8976
+
8977
+ return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
89108978
}
8979
+
8980
+static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
8981
+{
8982
+ if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
8983
+ JimAddErrorToStack(interp, script);
8984
+ return 0;
8985
+ }
8986
+ return 1;
8987
+}
8988
+
89118989
89128990
static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
89138991
{
89148992
cmdPtr->inUse++;
89158993
}
@@ -10901,11 +10979,11 @@
1090110979
return JIM_OK;
1090210980
}
1090310981
else {
1090410982
1090510983
if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10906
- Jim_SetResultFormatted(interp, "expected number but got \"%#s\"", objPtr);
10984
+ Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
1090710985
return JIM_ERR;
1090810986
}
1090910987
1091010988
Jim_FreeIntRep(interp, objPtr);
1091110989
}
@@ -12331,10 +12409,11 @@
1233112409
JIM_EXPROP_UNARYPLUS,
1233212410
1233312411
1233412412
JIM_EXPROP_FUNC_FIRST,
1233512413
JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12414
+ JIM_EXPROP_FUNC_WIDE,
1233612415
JIM_EXPROP_FUNC_ABS,
1233712416
JIM_EXPROP_FUNC_DOUBLE,
1233812417
JIM_EXPROP_FUNC_ROUND,
1233912418
JIM_EXPROP_FUNC_RAND,
1234012419
JIM_EXPROP_FUNC_SRAND,
@@ -12397,10 +12476,11 @@
1239712476
jim_wide wA, wC = 0;
1239812477
1239912478
if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
1240012479
switch (e->opcode) {
1240112480
case JIM_EXPROP_FUNC_INT:
12481
+ case JIM_EXPROP_FUNC_WIDE:
1240212482
case JIM_EXPROP_FUNC_ROUND:
1240312483
case JIM_EXPROP_UNARYPLUS:
1240412484
wC = wA;
1240512485
break;
1240612486
case JIM_EXPROP_FUNC_DOUBLE:
@@ -12421,10 +12501,11 @@
1242112501
}
1242212502
}
1242312503
else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
1242412504
switch (e->opcode) {
1242512505
case JIM_EXPROP_FUNC_INT:
12506
+ case JIM_EXPROP_FUNC_WIDE:
1242612507
wC = dA;
1242712508
break;
1242812509
case JIM_EXPROP_FUNC_ROUND:
1242912510
wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
1243012511
break;
@@ -13093,10 +13174,11 @@
1309313174
OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
1309413175
1309513176
1309613177
1309713178
OPRINIT("int", 200, 1, JimExprOpNumUnary),
13179
+ OPRINIT("wide", 200, 1, JimExprOpNumUnary),
1309813180
OPRINIT("abs", 200, 1, JimExprOpNumUnary),
1309913181
OPRINIT("double", 200, 1, JimExprOpNumUnary),
1310013182
OPRINIT("round", 200, 1, JimExprOpNumUnary),
1310113183
OPRINIT("rand", 200, 0, JimExprOpNone),
1310213184
OPRINIT("srand", 200, 1, JimExprOpIntUnary),
@@ -14750,15 +14832,13 @@
1475014832
ret = Jim_EvalObjVector(interp, objc + 1, nargv);
1475114833
Jim_Free(nargv);
1475214834
return ret;
1475314835
}
1475414836
14755
-static void JimAddErrorToStack(Jim_Interp *interp, int retcode, ScriptObj *script)
14837
+static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
1475614838
{
14757
- int rc = retcode;
14758
-
14759
- if (rc == JIM_ERR && !interp->errorFlag) {
14839
+ if (!interp->errorFlag) {
1476014840
1476114841
interp->errorFlag = 1;
1476214842
Jim_IncrRefCount(script->fileNameObj);
1476314843
Jim_DecrRefCount(interp, interp->errorFileNameObj);
1476414844
interp->errorFileNameObj = script->fileNameObj;
@@ -14768,11 +14848,11 @@
1476814848
1476914849
interp->addStackTrace++;
1477014850
}
1477114851
1477214852
14773
- if (rc == JIM_ERR && interp->addStackTrace > 0) {
14853
+ if (interp->addStackTrace > 0) {
1477414854
1477514855
1477614856
JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
1477714857
1477814858
if (Jim_Length(script->fileNameObj)) {
@@ -14781,16 +14861,10 @@
1478114861
1478214862
Jim_DecrRefCount(interp, interp->errorProc);
1478314863
interp->errorProc = interp->emptyObj;
1478414864
Jim_IncrRefCount(interp->errorProc);
1478514865
}
14786
- else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) {
14787
-
14788
- }
14789
- else {
14790
- interp->addStackTrace = 0;
14791
- }
1479214866
}
1479314867
1479414868
static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
1479514869
{
1479614870
Jim_Obj *objPtr;
@@ -14928,10 +15002,12 @@
1492815002
1492915003
static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
1493015004
{
1493115005
int retcode = JIM_OK;
1493215006
15007
+ JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15008
+
1493315009
if (listPtr->internalRep.listValue.len) {
1493415010
Jim_IncrRefCount(listPtr);
1493515011
retcode = JimInvokeCommand(interp,
1493615012
listPtr->internalRep.listValue.len,
1493715013
listPtr->internalRep.listValue.ele);
@@ -14958,12 +15034,12 @@
1495815034
if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
1495915035
return JimEvalObjList(interp, scriptObjPtr);
1496015036
}
1496115037
1496215038
Jim_IncrRefCount(scriptObjPtr);
14963
- script = Jim_GetScript(interp, scriptObjPtr);
14964
- if (script == NULL) {
15039
+ script = JimGetScript(interp, scriptObjPtr);
15040
+ if (!JimScriptValid(interp, script)) {
1496515041
Jim_DecrRefCount(interp, scriptObjPtr);
1496615042
return JIM_ERR;
1496715043
}
1496815044
1496915045
Jim_SetEmptyResult(interp);
@@ -15125,11 +15201,18 @@
1512515201
argv = sargv;
1512615202
}
1512715203
}
1512815204
1512915205
15130
- JimAddErrorToStack(interp, retcode, script);
15206
+ if (retcode == JIM_ERR) {
15207
+ JimAddErrorToStack(interp, script);
15208
+ }
15209
+
15210
+ else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15211
+
15212
+ interp->addStackTrace = 0;
15213
+ }
1513115214
1513215215
1513315216
interp->currentScriptObj = prevScriptObj;
1513415217
1513515218
Jim_FreeIntRep(interp, scriptObjPtr);
@@ -15276,11 +15359,11 @@
1527615359
callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
1527715360
callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
1527815361
callFramePtr->staticVars = cmd->u.proc.staticVars;
1527915362
1528015363
15281
- script = Jim_GetScript(interp, interp->currentScriptObj);
15364
+ script = JimGetScript(interp, interp->currentScriptObj);
1528215365
callFramePtr->fileNameObj = script->fileNameObj;
1528315366
callFramePtr->line = script->linenr;
1528415367
1528515368
Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
1528615369
Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
@@ -15473,18 +15556,10 @@
1547315556
1547415557
scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
1547515558
JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
1547615559
Jim_IncrRefCount(scriptObjPtr);
1547715560
15478
-
15479
- if (Jim_GetScript(interp, scriptObjPtr) == NULL) {
15480
-
15481
- JimAddErrorToStack(interp, JIM_ERR, (ScriptObj *)Jim_GetIntRepPtr(scriptObjPtr));
15482
- Jim_DecrRefCount(interp, scriptObjPtr);
15483
- return JIM_ERR;
15484
- }
15485
-
1548615561
prevScriptObj = interp->currentScriptObj;
1548715562
interp->currentScriptObj = scriptObjPtr;
1548815563
1548915564
retcode = Jim_EvalObj(interp, scriptObjPtr);
1549015565
@@ -16030,11 +16105,11 @@
1603016105
Jim_Obj *objPtr;
1603116106
int cmpOffset;
1603216107
1603316108
1603416109
expr = JimGetExpression(interp, argv[2]);
16035
- incrScript = Jim_GetScript(interp, argv[3]);
16110
+ incrScript = JimGetScript(interp, argv[3]);
1603616111
1603716112
1603816113
if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
1603916114
goto evalstart;
1604016115
}
@@ -17017,11 +17092,11 @@
1701717092
{
1701817093
Jim_Obj *stringObjPtr;
1701917094
int i;
1702017095
1702117096
if (argc < 2) {
17022
- Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
17097
+ Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
1702317098
return JIM_ERR;
1702417099
}
1702517100
if (argc == 2) {
1702617101
stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
1702717102
if (!stringObjPtr)
@@ -17066,11 +17141,11 @@
1706617141
static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1706717142
{
1706817143
int rc;
1706917144
1707017145
if (argc < 2) {
17071
- Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
17146
+ Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
1707217147
return JIM_ERR;
1707317148
}
1707417149
1707517150
if (argc == 2) {
1707617151
rc = Jim_EvalObj(interp, argv[1]);
@@ -17277,11 +17352,11 @@
1727717352
Jim_Obj *cmdList;
1727817353
Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
1727917354
1728017355
1728117356
cmdList = Jim_DuplicateObj(interp, prefixListObj);
17282
- ListInsertElements(cmdList, -1, argc - 1, argv + 1);
17357
+ Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
1728317358
1728417359
return JimEvalObjList(interp, cmdList);
1728517360
}
1728617361
1728717362
static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
@@ -17597,17 +17672,17 @@
1759717672
int len;
1759817673
int opt_case = 1;
1759917674
int option;
1760017675
static const char * const options[] = {
1760117676
"bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17602
- "map", "repeat", "reverse", "index", "first", "last",
17677
+ "map", "repeat", "reverse", "index", "first", "last", "cat",
1760317678
"trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
1760417679
};
1760517680
enum
1760617681
{
1760717682
OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17608
- OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST,
17683
+ OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
1760917684
OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
1761017685
};
1761117686
static const char * const nocase_options[] = {
1761217687
"-nocase", NULL
1761317688
};
@@ -17636,10 +17711,29 @@
1763617711
else {
1763717712
len = Jim_Length(argv[2]);
1763817713
}
1763917714
Jim_SetResultInt(interp, len);
1764017715
return JIM_OK;
17716
+
17717
+ case OPT_CAT:{
17718
+ Jim_Obj *objPtr;
17719
+ if (argc == 3) {
17720
+
17721
+ objPtr = argv[2];
17722
+ }
17723
+ else {
17724
+ int i;
17725
+
17726
+ objPtr = Jim_NewStringObj(interp, "", 0);
17727
+
17728
+ for (i = 2; i < argc; i++) {
17729
+ Jim_AppendObj(interp, objPtr, argv[i]);
17730
+ }
17731
+ }
17732
+ Jim_SetResult(interp, objPtr);
17733
+ return JIM_OK;
17734
+ }
1764117735
1764217736
case OPT_COMPARE:
1764317737
case OPT_EQUAL:
1764417738
{
1764517739
@@ -18619,38 +18713,47 @@
1861918713
case INFO_SCRIPT:
1862018714
if (argc != 2) {
1862118715
Jim_WrongNumArgs(interp, 2, argv, "");
1862218716
return JIM_ERR;
1862318717
}
18624
- Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj);
18718
+ Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
1862518719
break;
1862618720
1862718721
case INFO_SOURCE:{
18628
- int line;
18722
+ jim_wide line;
1862918723
Jim_Obj *resObjPtr;
1863018724
Jim_Obj *fileNameObj;
1863118725
18632
- if (argc != 3) {
18633
- Jim_WrongNumArgs(interp, 2, argv, "source");
18726
+ if (argc != 3 && argc != 5) {
18727
+ Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
1863418728
return JIM_ERR;
1863518729
}
18636
- if (argv[2]->typePtr == &sourceObjType) {
18637
- fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18638
- line = argv[2]->internalRep.sourceValue.lineNumber;
18639
- }
18640
- else if (argv[2]->typePtr == &scriptObjType) {
18641
- ScriptObj *script = Jim_GetScript(interp, argv[2]);
18642
- fileNameObj = script->fileNameObj;
18643
- line = script->firstline;
18730
+ if (argc == 5) {
18731
+ if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18732
+ return JIM_ERR;
18733
+ }
18734
+ resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18735
+ JimSetSourceInfo(interp, resObjPtr, argv[3], line);
1864418736
}
1864518737
else {
18646
- fileNameObj = interp->emptyObj;
18647
- line = 1;
18738
+ if (argv[2]->typePtr == &sourceObjType) {
18739
+ fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18740
+ line = argv[2]->internalRep.sourceValue.lineNumber;
18741
+ }
18742
+ else if (argv[2]->typePtr == &scriptObjType) {
18743
+ ScriptObj *script = JimGetScript(interp, argv[2]);
18744
+ fileNameObj = script->fileNameObj;
18745
+ line = script->firstline;
18746
+ }
18747
+ else {
18748
+ fileNameObj = interp->emptyObj;
18749
+ line = 1;
18750
+ }
18751
+ resObjPtr = Jim_NewListObj(interp, NULL, 0);
18752
+ Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18753
+ Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
1864818754
}
18649
- resObjPtr = Jim_NewListObj(interp, NULL, 0);
18650
- Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18651
- Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
1865218755
Jim_SetResult(interp, resObjPtr);
1865318756
break;
1865418757
}
1865518758
1865618759
case INFO_STACKTRACE:
@@ -21843,11 +21946,11 @@
2184321946
2184421947
if (Jim_InitStaticExtensions(interp) != JIM_OK) {
2184521948
JimPrintErrorMessage(interp);
2184621949
}
2184721950
21848
- Jim_SetVariableStrWithStr(interp, "jim_argv0", argv[0]);
21951
+ Jim_SetVariableStrWithStr(interp, "jim::argv0", argv[0]);
2184921952
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
2185021953
retcode = Jim_initjimshInit(interp);
2185121954
2185221955
if (argc == 1) {
2185321956
if (retcode == JIM_ERR) {
2185421957
--- autosetup/jimsh0.c
+++ autosetup/jimsh0.c
@@ -38,15 +38,17 @@
38 #define TCL_PLATFORM_PLATFORM "unix"
39 #define TCL_PLATFORM_PATH_SEPARATOR ":"
40 #define HAVE_VFORK
41 #define HAVE_WAITPID
42 #define HAVE_ISATTY
 
 
43 #define HAVE_SYS_TIME_H
44 #define HAVE_DIRENT_H
45 #define HAVE_UNISTD_H
46 #endif
47 #define JIM_VERSION 75
48 #ifndef JIM_WIN32COMPAT_H
49 #define JIM_WIN32COMPAT_H
50
51
52
@@ -113,10 +115,11 @@
113 int closedir(DIR *dir);
114 struct dirent *readdir(DIR *dir);
115
116 #elif defined(__MINGW32__)
117
 
118 #define strtod __strtod
119
120 #endif
121
122 #endif
@@ -1048,29 +1051,48 @@
1048 "\n"
1049 "\n"
1050 "\n"
1051 "proc _jimsh_init {} {\n"
1052 " rename _jimsh_init {}\n"
 
1053 "\n"
1054 "\n"
1055 " lappend p {*}[split [env JIMLIB {}] $::tcl_platform(pathSeparator)]\n"
1056 " lappend p {*}$::auto_path\n"
1057 " lappend p [file dirname [info nameofexecutable]]\n"
1058 " set ::auto_path $p\n"
 
 
 
 
 
 
 
 
 
1059 "\n"
1060 " if {$::tcl_interactive && [env HOME {}] ne \"\"} {\n"
 
 
 
 
 
 
 
 
1061 " foreach src {.jimrc jimrc.tcl} {\n"
1062 " if {[file exists [env HOME]/$src]} {\n"
1063 " uplevel #0 source [env HOME]/$src\n"
1064 " break\n"
1065 " }\n"
1066 " }\n"
1067 " }\n"
 
1068 "}\n"
1069 "\n"
1070 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1071 " set jim_argv0 [string map {\\\\ /} $jim_argv0]\n"
1072 "}\n"
1073 "\n"
1074 "_jimsh_init\n"
1075 );
1076 }
@@ -1091,11 +1113,11 @@
1091 "\n"
1092 "\n"
1093 "proc glob.globdir {dir pattern} {\n"
1094 " if {[file exists $dir/$pattern]} {\n"
1095 "\n"
1096 " return $pattern\n"
1097 " }\n"
1098 "\n"
1099 " set result {}\n"
1100 " set files [readdir $dir]\n"
1101 " lappend files . ..\n"
@@ -1153,11 +1175,11 @@
1153 " }\n"
1154 "\n"
1155 " foreach old $oldexp {\n"
1156 " lappend newexp $old$suf\n"
1157 " }\n"
1158 " linsert $newexp 0 $rest\n"
1159 "}\n"
1160 "\n"
1161 "\n"
1162 "\n"
1163 "proc glob.glob {base pattern} {\n"
@@ -1200,10 +1222,11 @@
1200 "\n"
1201 "\n"
1202 "proc glob {args} {\n"
1203 " set nocomplain 0\n"
1204 " set base \"\"\n"
 
1205 "\n"
1206 " set n 0\n"
1207 " foreach arg $args {\n"
1208 " if {[info exists param]} {\n"
1209 " set $param $arg\n"
@@ -1217,21 +1240,20 @@
1217 " set param base\n"
1218 " }\n"
1219 " -n* {\n"
1220 " set nocomplain 1\n"
1221 " }\n"
1222 " -t* {\n"
1223 "\n"
1224 " }\n"
1225 "\n"
1226 " -* {\n"
1227 " return -code error \"bad option \\\"$switch\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1228 " }\n"
1229 " -- {\n"
1230 " incr n\n"
1231 " break\n"
1232 " }\n"
 
 
 
1233 " * {\n"
1234 " break\n"
1235 " }\n"
1236 " }\n"
1237 " incr n\n"
@@ -1245,29 +1267,35 @@
1245 "\n"
1246 " set args [lrange $args $n end]\n"
1247 "\n"
1248 " set result {}\n"
1249 " foreach pattern $args {\n"
1250 " set pattern [string map {\n"
1251 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1252 " } $pattern]\n"
1253 " set patexps [lassign [glob.explode $pattern] rest]\n"
1254 " if {$rest ne \"\"} {\n"
1255 " return -code error \"unmatched close brace in glob pattern\"\n"
1256 " }\n"
1257 " foreach patexp $patexps {\n"
1258 " set patexp [string map {\n"
1259 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1260 " } $patexp]\n"
1261 " foreach {realname name} [glob.glob $base $patexp] {\n"
1262 " lappend result $name\n"
 
 
 
 
 
1263 " }\n"
1264 " }\n"
1265 " }\n"
1266 "\n"
1267 " if {!$nocomplain && [llength $result] == 0} {\n"
1268 " return -code error \"no files matched glob patterns\"\n"
 
1269 " }\n"
1270 "\n"
1271 " return $result\n"
1272 "}\n"
1273 );
@@ -1348,11 +1376,11 @@
1348 "\n"
1349 " lappend stacktrace {*}[stacktrace 1]\n"
1350 " }\n"
1351 " lassign $stacktrace p f l\n"
1352 " if {$f ne \"\"} {\n"
1353 " set result \"Runtime Error: $f:$l: \"\n"
1354 " }\n"
1355 " append result \"$msg\\n\"\n"
1356 " append result [stackdump $stacktrace]\n"
1357 "\n"
1358 "\n"
@@ -1360,22 +1388,13 @@
1360 "}\n"
1361 "\n"
1362 "\n"
1363 "\n"
1364 "proc {info nameofexecutable} {} {\n"
1365 " if {[info exists ::jim_argv0]} {\n"
1366 " if {[string match \"*/*\" $::jim_argv0]} {\n"
1367 " return [file join [pwd] $::jim_argv0]\n"
1368 " }\n"
1369 " foreach path [split [env PATH \"\"] $::tcl_platform(pathSeparator)] {\n"
1370 " set exec [file join [pwd] [string map {\\\\ /} $path] $::jim_argv0]\n"
1371 " if {[file executable $exec]} {\n"
1372 " return $exec\n"
1373 " }\n"
1374 " }\n"
1375 " }\n"
1376 " return \"\"\n"
1377 "}\n"
1378 "\n"
1379 "\n"
1380 "proc {dict with} {&dictVar {args key} script} {\n"
1381 " set keys {}\n"
@@ -1587,11 +1606,11 @@
1587 " try {\n"
1588 " if {$force ni {{} -force}} {\n"
1589 " error \"bad option \\\"$force\\\": should be -force\"\n"
1590 " }\n"
1591 "\n"
1592 " set in [open $source]\n"
1593 "\n"
1594 " if {[file exists $target]} {\n"
1595 " if {$force eq \"\"} {\n"
1596 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1597 " }\n"
@@ -1605,11 +1624,11 @@
1605 " file stat $target ts\n"
1606 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1607 " return\n"
1608 " }\n"
1609 " }\n"
1610 " set out [open $target w]\n"
1611 " $in copyto $out\n"
1612 " $out close\n"
1613 " } on error {msg opts} {\n"
1614 " incr opts(-level)\n"
1615 " return {*}$opts $msg\n"
@@ -1691,11 +1710,11 @@
1691 " }\n"
1692 " if {[llength $args] == 0} {\n"
1693 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1694 " }\n"
1695 " set args [lassign $args script]\n"
1696 " set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts]\n"
1697 "\n"
1698 " set handled 0\n"
1699 "\n"
1700 " foreach {on codes vars script} $args {\n"
1701 " switch -- $on \\\n"
@@ -1709,16 +1728,16 @@
1709 " if {$optsvar ne \"\"} {\n"
1710 " upvar $optsvar hopts\n"
1711 " set hopts $opts\n"
1712 " }\n"
1713 "\n"
1714 " set code [catch [list uplevel 1 $script] msg opts]\n"
1715 " incr handled\n"
1716 " }\n"
1717 " } \\\n"
1718 " finally {\n"
1719 " set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]\n"
1720 " if {$finalcode} {\n"
1721 "\n"
1722 " set code $finalcode\n"
1723 " set msg $finalmsg\n"
1724 " set opts $finalopts\n"
@@ -2502,10 +2521,11 @@
2502 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2503
2504 return JIM_OK;
2505 }
2506
 
2507 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2508 const char *hdlfmt, int family, const char *mode[2])
2509 {
2510 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) {
2511 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
@@ -2522,10 +2542,11 @@
2522 close(p[0]);
2523 close(p[1]);
2524 JimAioSetError(interp, NULL);
2525 return JIM_ERR;
2526 }
 
2527
2528
2529 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
2530 {
2531 #ifdef HAVE_MKSTEMP
@@ -2552,19 +2573,19 @@
2552
2553
2554 fd = mkstemp(filenameObj->bytes);
2555 umask(mask);
2556 if (fd < 0) {
2557 Jim_SetResultString(interp, "Failed to create tempfile", -1);
2558 Jim_FreeNewObj(interp, filenameObj);
2559 return -1;
2560 }
2561
2562 Jim_SetResult(interp, filenameObj);
2563 return fd;
2564 #else
2565 Jim_SetResultString(interp, "tempfile not supported", -1);
2566 return -1;
2567 #endif
2568 }
2569
2570 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
@@ -3176,10 +3197,16 @@
3176
3177 # ifndef MAXPATHLEN
3178 # define MAXPATHLEN JIM_PATH_LEN
3179 # endif
3180
 
 
 
 
 
 
3181
3182 static const char *JimGetFileType(int mode)
3183 {
3184 if (S_ISREG(mode)) {
3185 return "file";
@@ -3281,16 +3308,14 @@
3281 Jim_SetResultString(interp, ".", -1);
3282 }
3283 else if (p == path) {
3284 Jim_SetResultString(interp, "/", -1);
3285 }
3286 #if defined(__MINGW32__) || defined(_MSC_VER)
3287 else if (p[-1] == ':') {
3288
3289 Jim_SetResultString(interp, path, p - path + 1);
3290 }
3291 #endif
3292 else {
3293 Jim_SetResultString(interp, path, p - path);
3294 }
3295 return JIM_OK;
3296 }
@@ -3373,16 +3398,14 @@
3373
3374 if (*part == '/') {
3375
3376 last = newname;
3377 }
3378 #if defined(__MINGW32__) || defined(_MSC_VER)
3379 else if (strchr(part, ':')) {
3380
3381 last = newname;
3382 }
3383 #endif
3384 else if (part[0] == '.') {
3385 if (part[1] == '/') {
3386 part += 2;
3387 len -= 2;
3388 }
@@ -3407,11 +3430,14 @@
3407 last += len;
3408 }
3409
3410
3411 if (last > newname + 1 && last[-1] == '/') {
3412 *--last = 0;
 
 
 
3413 }
3414 }
3415
3416 *last = 0;
3417
@@ -3591,10 +3617,48 @@
3591 return JIM_ERR;
3592 }
3593
3594 return JIM_OK;
3595 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3596
3597 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3598 {
3599 const char *path = Jim_String(filename);
3600
@@ -3889,10 +3953,19 @@
3889 file_cmd_rename,
3890 2,
3891 3,
3892
3893 },
 
 
 
 
 
 
 
 
 
3894 #if defined(HAVE_READLINK)
3895 { "readlink",
3896 "name",
3897 file_cmd_readlink,
3898 1,
@@ -3982,19 +4055,17 @@
3982 if (getcwd(cwd, MAXPATHLEN) == NULL) {
3983 Jim_SetResultString(interp, "Failed to get pwd", -1);
3984 Jim_Free(cwd);
3985 return JIM_ERR;
3986 }
3987 #if defined(__MINGW32__) || defined(_MSC_VER)
3988 {
3989
3990 char *p = cwd;
3991 while ((p = strchr(p, '\\')) != NULL) {
3992 *p++ = '/';
3993 }
3994 }
3995 #endif
3996
3997 Jim_SetResultString(interp, cwd, -1);
3998
3999 Jim_Free(cwd);
4000 return JIM_OK;
@@ -5322,22 +5393,20 @@
5322
5323 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5324 {
5325 int fd = Jim_MakeTempFile(interp, NULL);
5326
5327 if (fd == JIM_BAD_FD) {
5328 Jim_SetResultErrno(interp, "couldn't create temp file");
5329 return -1;
5330 }
5331 unlink(Jim_String(Jim_GetResult(interp)));
5332 if (contents) {
5333 if (write(fd, contents, len) != len) {
5334 Jim_SetResultErrno(interp, "couldn't write temp file");
5335 close(fd);
5336 return -1;
5337 }
5338 lseek(fd, 0L, SEEK_SET);
5339 }
5340 return fd;
5341 }
5342
5343 static char **JimSaveEnv(char **env)
@@ -7768,10 +7837,11 @@
7768
7769 const char *Jim_String(Jim_Obj *objPtr)
7770 {
7771 if (objPtr->bytes == NULL) {
7772
 
7773 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7774 objPtr->typePtr->updateStringProc(objPtr);
7775 }
7776 return objPtr->bytes;
7777 }
@@ -8530,11 +8600,11 @@
8530 return objPtr;
8531 }
8532
8533 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8534 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8535 static int JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8536 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8537
8538 static const Jim_ObjType scriptObjType = {
8539 "script",
8540 FreeScriptInternalRep,
@@ -8558,10 +8628,11 @@
8558 int inUse; /* Used to share a ScriptObj. Currently
8559 only used by Jim_EvalObj() as protection against
8560 shimmering of the currently evaluated object. */
8561 int firstline;
8562 int linenr;
 
8563 } ScriptObj;
8564
8565 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8566 {
8567 int i;
@@ -8836,19 +8907,18 @@
8836 }
8837
8838 script->len = i;
8839 }
8840
8841 static int JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
8842 {
8843 int scriptTextLen;
8844 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8845 struct JimParserCtx parser;
8846 struct ScriptObj *script;
8847 ParseTokenList tokenlist;
8848 int line = 1;
8849 int retcode = JIM_OK;
8850
8851
8852 if (objPtr->typePtr == &sourceObjType) {
8853 line = objPtr->internalRep.sourceValue.lineNumber;
8854 }
@@ -8861,12 +8931,10 @@
8861 JimParseScript(&parser);
8862 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
8863 parser.tline);
8864 }
8865
8866 retcode = JimParseCheckMissing(interp, parser.missing.ch);
8867
8868
8869 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
8870
8871
8872 script = Jim_Alloc(sizeof(*script));
@@ -8876,12 +8944,13 @@
8876 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
8877 }
8878 else {
8879 script->fileNameObj = interp->emptyObj;
8880 }
8881 script->linenr = parser.missing.line;
8882 Jim_IncrRefCount(script->fileNameObj);
 
 
8883
8884 ScriptObjAddTokens(interp, script, &tokenlist);
8885
8886
8887 ScriptTokenListFree(&tokenlist);
@@ -8888,28 +8957,37 @@
8888
8889
8890 Jim_FreeIntRep(interp, objPtr);
8891 Jim_SetIntRepPtr(objPtr, script);
8892 objPtr->typePtr = &scriptObjType;
8893
8894 return retcode;
8895 }
8896
8897 ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr)
 
 
8898 {
8899 if (objPtr == interp->emptyObj) {
8900
8901 objPtr = interp->nullScriptObj;
8902 }
8903
8904 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
8905 if (JimSetScriptFromAny(interp, objPtr) == JIM_ERR) {
8906 return NULL;
8907 }
8908 }
8909 return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
 
8910 }
 
 
 
 
 
 
 
 
 
 
8911
8912 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
8913 {
8914 cmdPtr->inUse++;
8915 }
@@ -10901,11 +10979,11 @@
10901 return JIM_OK;
10902 }
10903 else {
10904
10905 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10906 Jim_SetResultFormatted(interp, "expected number but got \"%#s\"", objPtr);
10907 return JIM_ERR;
10908 }
10909
10910 Jim_FreeIntRep(interp, objPtr);
10911 }
@@ -12331,10 +12409,11 @@
12331 JIM_EXPROP_UNARYPLUS,
12332
12333
12334 JIM_EXPROP_FUNC_FIRST,
12335 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
 
12336 JIM_EXPROP_FUNC_ABS,
12337 JIM_EXPROP_FUNC_DOUBLE,
12338 JIM_EXPROP_FUNC_ROUND,
12339 JIM_EXPROP_FUNC_RAND,
12340 JIM_EXPROP_FUNC_SRAND,
@@ -12397,10 +12476,11 @@
12397 jim_wide wA, wC = 0;
12398
12399 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12400 switch (e->opcode) {
12401 case JIM_EXPROP_FUNC_INT:
 
12402 case JIM_EXPROP_FUNC_ROUND:
12403 case JIM_EXPROP_UNARYPLUS:
12404 wC = wA;
12405 break;
12406 case JIM_EXPROP_FUNC_DOUBLE:
@@ -12421,10 +12501,11 @@
12421 }
12422 }
12423 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12424 switch (e->opcode) {
12425 case JIM_EXPROP_FUNC_INT:
 
12426 wC = dA;
12427 break;
12428 case JIM_EXPROP_FUNC_ROUND:
12429 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12430 break;
@@ -13093,10 +13174,11 @@
13093 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13094
13095
13096
13097 OPRINIT("int", 200, 1, JimExprOpNumUnary),
 
13098 OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13099 OPRINIT("double", 200, 1, JimExprOpNumUnary),
13100 OPRINIT("round", 200, 1, JimExprOpNumUnary),
13101 OPRINIT("rand", 200, 0, JimExprOpNone),
13102 OPRINIT("srand", 200, 1, JimExprOpIntUnary),
@@ -14750,15 +14832,13 @@
14750 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14751 Jim_Free(nargv);
14752 return ret;
14753 }
14754
14755 static void JimAddErrorToStack(Jim_Interp *interp, int retcode, ScriptObj *script)
14756 {
14757 int rc = retcode;
14758
14759 if (rc == JIM_ERR && !interp->errorFlag) {
14760
14761 interp->errorFlag = 1;
14762 Jim_IncrRefCount(script->fileNameObj);
14763 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14764 interp->errorFileNameObj = script->fileNameObj;
@@ -14768,11 +14848,11 @@
14768
14769 interp->addStackTrace++;
14770 }
14771
14772
14773 if (rc == JIM_ERR && interp->addStackTrace > 0) {
14774
14775
14776 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14777
14778 if (Jim_Length(script->fileNameObj)) {
@@ -14781,16 +14861,10 @@
14781
14782 Jim_DecrRefCount(interp, interp->errorProc);
14783 interp->errorProc = interp->emptyObj;
14784 Jim_IncrRefCount(interp->errorProc);
14785 }
14786 else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) {
14787
14788 }
14789 else {
14790 interp->addStackTrace = 0;
14791 }
14792 }
14793
14794 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14795 {
14796 Jim_Obj *objPtr;
@@ -14928,10 +15002,12 @@
14928
14929 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
14930 {
14931 int retcode = JIM_OK;
14932
 
 
14933 if (listPtr->internalRep.listValue.len) {
14934 Jim_IncrRefCount(listPtr);
14935 retcode = JimInvokeCommand(interp,
14936 listPtr->internalRep.listValue.len,
14937 listPtr->internalRep.listValue.ele);
@@ -14958,12 +15034,12 @@
14958 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
14959 return JimEvalObjList(interp, scriptObjPtr);
14960 }
14961
14962 Jim_IncrRefCount(scriptObjPtr);
14963 script = Jim_GetScript(interp, scriptObjPtr);
14964 if (script == NULL) {
14965 Jim_DecrRefCount(interp, scriptObjPtr);
14966 return JIM_ERR;
14967 }
14968
14969 Jim_SetEmptyResult(interp);
@@ -15125,11 +15201,18 @@
15125 argv = sargv;
15126 }
15127 }
15128
15129
15130 JimAddErrorToStack(interp, retcode, script);
 
 
 
 
 
 
 
15131
15132
15133 interp->currentScriptObj = prevScriptObj;
15134
15135 Jim_FreeIntRep(interp, scriptObjPtr);
@@ -15276,11 +15359,11 @@
15276 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15277 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15278 callFramePtr->staticVars = cmd->u.proc.staticVars;
15279
15280
15281 script = Jim_GetScript(interp, interp->currentScriptObj);
15282 callFramePtr->fileNameObj = script->fileNameObj;
15283 callFramePtr->line = script->linenr;
15284
15285 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15286 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
@@ -15473,18 +15556,10 @@
15473
15474 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15475 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15476 Jim_IncrRefCount(scriptObjPtr);
15477
15478
15479 if (Jim_GetScript(interp, scriptObjPtr) == NULL) {
15480
15481 JimAddErrorToStack(interp, JIM_ERR, (ScriptObj *)Jim_GetIntRepPtr(scriptObjPtr));
15482 Jim_DecrRefCount(interp, scriptObjPtr);
15483 return JIM_ERR;
15484 }
15485
15486 prevScriptObj = interp->currentScriptObj;
15487 interp->currentScriptObj = scriptObjPtr;
15488
15489 retcode = Jim_EvalObj(interp, scriptObjPtr);
15490
@@ -16030,11 +16105,11 @@
16030 Jim_Obj *objPtr;
16031 int cmpOffset;
16032
16033
16034 expr = JimGetExpression(interp, argv[2]);
16035 incrScript = Jim_GetScript(interp, argv[3]);
16036
16037
16038 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16039 goto evalstart;
16040 }
@@ -17017,11 +17092,11 @@
17017 {
17018 Jim_Obj *stringObjPtr;
17019 int i;
17020
17021 if (argc < 2) {
17022 Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
17023 return JIM_ERR;
17024 }
17025 if (argc == 2) {
17026 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17027 if (!stringObjPtr)
@@ -17066,11 +17141,11 @@
17066 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17067 {
17068 int rc;
17069
17070 if (argc < 2) {
17071 Jim_WrongNumArgs(interp, 1, argv, "script ?...?");
17072 return JIM_ERR;
17073 }
17074
17075 if (argc == 2) {
17076 rc = Jim_EvalObj(interp, argv[1]);
@@ -17277,11 +17352,11 @@
17277 Jim_Obj *cmdList;
17278 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17279
17280
17281 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17282 ListInsertElements(cmdList, -1, argc - 1, argv + 1);
17283
17284 return JimEvalObjList(interp, cmdList);
17285 }
17286
17287 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
@@ -17597,17 +17672,17 @@
17597 int len;
17598 int opt_case = 1;
17599 int option;
17600 static const char * const options[] = {
17601 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17602 "map", "repeat", "reverse", "index", "first", "last",
17603 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17604 };
17605 enum
17606 {
17607 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17608 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST,
17609 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17610 };
17611 static const char * const nocase_options[] = {
17612 "-nocase", NULL
17613 };
@@ -17636,10 +17711,29 @@
17636 else {
17637 len = Jim_Length(argv[2]);
17638 }
17639 Jim_SetResultInt(interp, len);
17640 return JIM_OK;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17641
17642 case OPT_COMPARE:
17643 case OPT_EQUAL:
17644 {
17645
@@ -18619,38 +18713,47 @@
18619 case INFO_SCRIPT:
18620 if (argc != 2) {
18621 Jim_WrongNumArgs(interp, 2, argv, "");
18622 return JIM_ERR;
18623 }
18624 Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj);
18625 break;
18626
18627 case INFO_SOURCE:{
18628 int line;
18629 Jim_Obj *resObjPtr;
18630 Jim_Obj *fileNameObj;
18631
18632 if (argc != 3) {
18633 Jim_WrongNumArgs(interp, 2, argv, "source");
18634 return JIM_ERR;
18635 }
18636 if (argv[2]->typePtr == &sourceObjType) {
18637 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18638 line = argv[2]->internalRep.sourceValue.lineNumber;
18639 }
18640 else if (argv[2]->typePtr == &scriptObjType) {
18641 ScriptObj *script = Jim_GetScript(interp, argv[2]);
18642 fileNameObj = script->fileNameObj;
18643 line = script->firstline;
18644 }
18645 else {
18646 fileNameObj = interp->emptyObj;
18647 line = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18648 }
18649 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18650 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18651 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18652 Jim_SetResult(interp, resObjPtr);
18653 break;
18654 }
18655
18656 case INFO_STACKTRACE:
@@ -21843,11 +21946,11 @@
21843
21844 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
21845 JimPrintErrorMessage(interp);
21846 }
21847
21848 Jim_SetVariableStrWithStr(interp, "jim_argv0", argv[0]);
21849 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
21850 retcode = Jim_initjimshInit(interp);
21851
21852 if (argc == 1) {
21853 if (retcode == JIM_ERR) {
21854
--- autosetup/jimsh0.c
+++ autosetup/jimsh0.c
@@ -38,15 +38,17 @@
38 #define TCL_PLATFORM_PLATFORM "unix"
39 #define TCL_PLATFORM_PATH_SEPARATOR ":"
40 #define HAVE_VFORK
41 #define HAVE_WAITPID
42 #define HAVE_ISATTY
43 #define HAVE_MKSTEMP
44 #define HAVE_LINK
45 #define HAVE_SYS_TIME_H
46 #define HAVE_DIRENT_H
47 #define HAVE_UNISTD_H
48 #endif
49 #define JIM_VERSION 76
50 #ifndef JIM_WIN32COMPAT_H
51 #define JIM_WIN32COMPAT_H
52
53
54
@@ -113,10 +115,11 @@
115 int closedir(DIR *dir);
116 struct dirent *readdir(DIR *dir);
117
118 #elif defined(__MINGW32__)
119
120 #include <stdlib.h>
121 #define strtod __strtod
122
123 #endif
124
125 #endif
@@ -1048,29 +1051,48 @@
1051 "\n"
1052 "\n"
1053 "\n"
1054 "proc _jimsh_init {} {\n"
1055 " rename _jimsh_init {}\n"
1056 " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
1057 "\n"
1058 "\n"
1059 " if {[exists jim::argv0]} {\n"
1060 " if {[string match \"*/*\" $jim::argv0]} {\n"
1061 " set jim::exe [file join [pwd] $jim::argv0]\n"
1062 " } else {\n"
1063 " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n"
1064 " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n"
1065 " if {[file executable $exec]} {\n"
1066 " set jim::exe $exec\n"
1067 " break\n"
1068 " }\n"
1069 " }\n"
1070 " }\n"
1071 " }\n"
1072 "\n"
1073 "\n"
1074 " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
1075 " if {[exists jim::exe]} {\n"
1076 " lappend p [file dirname $jim::exe]\n"
1077 " }\n"
1078 " lappend p {*}$auto_path\n"
1079 " set auto_path $p\n"
1080 "\n"
1081 " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
1082 " foreach src {.jimrc jimrc.tcl} {\n"
1083 " if {[file exists [env HOME]/$src]} {\n"
1084 " uplevel #0 source [env HOME]/$src\n"
1085 " break\n"
1086 " }\n"
1087 " }\n"
1088 " }\n"
1089 " return \"\"\n"
1090 "}\n"
1091 "\n"
1092 "if {$tcl_platform(platform) eq \"windows\"} {\n"
1093 " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
1094 "}\n"
1095 "\n"
1096 "_jimsh_init\n"
1097 );
1098 }
@@ -1091,11 +1113,11 @@
1113 "\n"
1114 "\n"
1115 "proc glob.globdir {dir pattern} {\n"
1116 " if {[file exists $dir/$pattern]} {\n"
1117 "\n"
1118 " return [list $pattern]\n"
1119 " }\n"
1120 "\n"
1121 " set result {}\n"
1122 " set files [readdir $dir]\n"
1123 " lappend files . ..\n"
@@ -1153,11 +1175,11 @@
1175 " }\n"
1176 "\n"
1177 " foreach old $oldexp {\n"
1178 " lappend newexp $old$suf\n"
1179 " }\n"
1180 " list $rest {*}$newexp\n"
1181 "}\n"
1182 "\n"
1183 "\n"
1184 "\n"
1185 "proc glob.glob {base pattern} {\n"
@@ -1200,10 +1222,11 @@
1222 "\n"
1223 "\n"
1224 "proc glob {args} {\n"
1225 " set nocomplain 0\n"
1226 " set base \"\"\n"
1227 " set tails 0\n"
1228 "\n"
1229 " set n 0\n"
1230 " foreach arg $args {\n"
1231 " if {[info exists param]} {\n"
1232 " set $param $arg\n"
@@ -1217,21 +1240,20 @@
1240 " set param base\n"
1241 " }\n"
1242 " -n* {\n"
1243 " set nocomplain 1\n"
1244 " }\n"
1245 " -ta* {\n"
1246 " set tails 1\n"
 
 
 
 
1247 " }\n"
1248 " -- {\n"
1249 " incr n\n"
1250 " break\n"
1251 " }\n"
1252 " -* {\n"
1253 " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
1254 " }\n"
1255 " * {\n"
1256 " break\n"
1257 " }\n"
1258 " }\n"
1259 " incr n\n"
@@ -1245,29 +1267,35 @@
1267 "\n"
1268 " set args [lrange $args $n end]\n"
1269 "\n"
1270 " set result {}\n"
1271 " foreach pattern $args {\n"
1272 " set escpattern [string map {\n"
1273 " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
1274 " } $pattern]\n"
1275 " set patexps [lassign [glob.explode $escpattern] rest]\n"
1276 " if {$rest ne \"\"} {\n"
1277 " return -code error \"unmatched close brace in glob pattern\"\n"
1278 " }\n"
1279 " foreach patexp $patexps {\n"
1280 " set patexp [string map {\n"
1281 " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
1282 " } $patexp]\n"
1283 " foreach {realname name} [glob.glob $base $patexp] {\n"
1284 " incr n\n"
1285 " if {$tails} {\n"
1286 " lappend result $name\n"
1287 " } else {\n"
1288 " lappend result [file join $base $name]\n"
1289 " }\n"
1290 " }\n"
1291 " }\n"
1292 " }\n"
1293 "\n"
1294 " if {!$nocomplain && [llength $result] == 0} {\n"
1295 " set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
1296 " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
1297 " }\n"
1298 "\n"
1299 " return $result\n"
1300 "}\n"
1301 );
@@ -1348,11 +1376,11 @@
1376 "\n"
1377 " lappend stacktrace {*}[stacktrace 1]\n"
1378 " }\n"
1379 " lassign $stacktrace p f l\n"
1380 " if {$f ne \"\"} {\n"
1381 " set result \"$f:$l: Error: \"\n"
1382 " }\n"
1383 " append result \"$msg\\n\"\n"
1384 " append result [stackdump $stacktrace]\n"
1385 "\n"
1386 "\n"
@@ -1360,22 +1388,13 @@
1388 "}\n"
1389 "\n"
1390 "\n"
1391 "\n"
1392 "proc {info nameofexecutable} {} {\n"
1393 " if {[exists ::jim::exe]} {\n"
1394 " return $::jim::exe\n"
 
 
 
 
 
 
 
 
1395 " }\n"
 
1396 "}\n"
1397 "\n"
1398 "\n"
1399 "proc {dict with} {&dictVar {args key} script} {\n"
1400 " set keys {}\n"
@@ -1587,11 +1606,11 @@
1606 " try {\n"
1607 " if {$force ni {{} -force}} {\n"
1608 " error \"bad option \\\"$force\\\": should be -force\"\n"
1609 " }\n"
1610 "\n"
1611 " set in [open $source rb]\n"
1612 "\n"
1613 " if {[file exists $target]} {\n"
1614 " if {$force eq \"\"} {\n"
1615 " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
1616 " }\n"
@@ -1605,11 +1624,11 @@
1624 " file stat $target ts\n"
1625 " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
1626 " return\n"
1627 " }\n"
1628 " }\n"
1629 " set out [open $target wb]\n"
1630 " $in copyto $out\n"
1631 " $out close\n"
1632 " } on error {msg opts} {\n"
1633 " incr opts(-level)\n"
1634 " return {*}$opts $msg\n"
@@ -1691,11 +1710,11 @@
1710 " }\n"
1711 " if {[llength $args] == 0} {\n"
1712 " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n"
1713 " }\n"
1714 " set args [lassign $args script]\n"
1715 " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n"
1716 "\n"
1717 " set handled 0\n"
1718 "\n"
1719 " foreach {on codes vars script} $args {\n"
1720 " switch -- $on \\\n"
@@ -1709,16 +1728,16 @@
1728 " if {$optsvar ne \"\"} {\n"
1729 " upvar $optsvar hopts\n"
1730 " set hopts $opts\n"
1731 " }\n"
1732 "\n"
1733 " set code [catch {uplevel 1 $script} msg opts]\n"
1734 " incr handled\n"
1735 " }\n"
1736 " } \\\n"
1737 " finally {\n"
1738 " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n"
1739 " if {$finalcode} {\n"
1740 "\n"
1741 " set code $finalcode\n"
1742 " set msg $finalmsg\n"
1743 " set opts $finalopts\n"
@@ -2502,10 +2521,11 @@
2521 Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1)));
2522
2523 return JIM_OK;
2524 }
2525
2526 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
2527 static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
2528 const char *hdlfmt, int family, const char *mode[2])
2529 {
2530 if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) {
2531 Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
@@ -2522,10 +2542,11 @@
2542 close(p[0]);
2543 close(p[1]);
2544 JimAioSetError(interp, NULL);
2545 return JIM_ERR;
2546 }
2547 #endif
2548
2549
2550 int Jim_MakeTempFile(Jim_Interp *interp, const char *template)
2551 {
2552 #ifdef HAVE_MKSTEMP
@@ -2552,19 +2573,19 @@
2573
2574
2575 fd = mkstemp(filenameObj->bytes);
2576 umask(mask);
2577 if (fd < 0) {
2578 JimAioSetError(interp, filenameObj);
2579 Jim_FreeNewObj(interp, filenameObj);
2580 return -1;
2581 }
2582
2583 Jim_SetResult(interp, filenameObj);
2584 return fd;
2585 #else
2586 Jim_SetResultString(interp, "platform has no tempfile support", -1);
2587 return -1;
2588 #endif
2589 }
2590
2591 FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
@@ -3176,10 +3197,16 @@
3197
3198 # ifndef MAXPATHLEN
3199 # define MAXPATHLEN JIM_PATH_LEN
3200 # endif
3201
3202 #if defined(__MINGW32__) || defined(_MSC_VER)
3203 #define ISWINDOWS 1
3204 #else
3205 #define ISWINDOWS 0
3206 #endif
3207
3208
3209 static const char *JimGetFileType(int mode)
3210 {
3211 if (S_ISREG(mode)) {
3212 return "file";
@@ -3281,16 +3308,14 @@
3308 Jim_SetResultString(interp, ".", -1);
3309 }
3310 else if (p == path) {
3311 Jim_SetResultString(interp, "/", -1);
3312 }
3313 else if (ISWINDOWS && p[-1] == ':') {
 
3314
3315 Jim_SetResultString(interp, path, p - path + 1);
3316 }
 
3317 else {
3318 Jim_SetResultString(interp, path, p - path);
3319 }
3320 return JIM_OK;
3321 }
@@ -3373,16 +3398,14 @@
3398
3399 if (*part == '/') {
3400
3401 last = newname;
3402 }
3403 else if (ISWINDOWS && strchr(part, ':')) {
 
3404
3405 last = newname;
3406 }
 
3407 else if (part[0] == '.') {
3408 if (part[1] == '/') {
3409 part += 2;
3410 len -= 2;
3411 }
@@ -3407,11 +3430,14 @@
3430 last += len;
3431 }
3432
3433
3434 if (last > newname + 1 && last[-1] == '/') {
3435
3436 if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
3437 *--last = 0;
3438 }
3439 }
3440 }
3441
3442 *last = 0;
3443
@@ -3591,10 +3617,48 @@
3617 return JIM_ERR;
3618 }
3619
3620 return JIM_OK;
3621 }
3622
3623 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3624 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
3625 {
3626 int ret;
3627 const char *source;
3628 const char *dest;
3629 static const char * const options[] = { "-hard", "-symbolic", NULL };
3630 enum { OPT_HARD, OPT_SYMBOLIC, };
3631 int option = OPT_HARD;
3632
3633 if (argc == 3) {
3634 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
3635 return JIM_ERR;
3636 }
3637 argv++;
3638 argc--;
3639 }
3640
3641 dest = Jim_String(argv[0]);
3642 source = Jim_String(argv[1]);
3643
3644 if (option == OPT_HARD) {
3645 ret = link(source, dest);
3646 }
3647 else {
3648 ret = symlink(source, dest);
3649 }
3650
3651 if (ret != 0) {
3652 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
3653 strerror(errno));
3654 return JIM_ERR;
3655 }
3656
3657 return JIM_OK;
3658 }
3659 #endif
3660
3661 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
3662 {
3663 const char *path = Jim_String(filename);
3664
@@ -3889,10 +3953,19 @@
3953 file_cmd_rename,
3954 2,
3955 3,
3956
3957 },
3958 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
3959 { "link",
3960 "?-symbolic|-hard? newname target",
3961 file_cmd_link,
3962 2,
3963 3,
3964
3965 },
3966 #endif
3967 #if defined(HAVE_READLINK)
3968 { "readlink",
3969 "name",
3970 file_cmd_readlink,
3971 1,
@@ -3982,19 +4055,17 @@
4055 if (getcwd(cwd, MAXPATHLEN) == NULL) {
4056 Jim_SetResultString(interp, "Failed to get pwd", -1);
4057 Jim_Free(cwd);
4058 return JIM_ERR;
4059 }
4060 else if (ISWINDOWS) {
 
4061
4062 char *p = cwd;
4063 while ((p = strchr(p, '\\')) != NULL) {
4064 *p++ = '/';
4065 }
4066 }
 
4067
4068 Jim_SetResultString(interp, cwd, -1);
4069
4070 Jim_Free(cwd);
4071 return JIM_OK;
@@ -5322,22 +5393,20 @@
5393
5394 static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
5395 {
5396 int fd = Jim_MakeTempFile(interp, NULL);
5397
5398 if (fd != JIM_BAD_FD) {
5399 unlink(Jim_String(Jim_GetResult(interp)));
5400 if (contents) {
5401 if (write(fd, contents, len) != len) {
5402 Jim_SetResultErrno(interp, "couldn't write temp file");
5403 close(fd);
5404 return -1;
5405 }
5406 lseek(fd, 0L, SEEK_SET);
5407 }
 
 
5408 }
5409 return fd;
5410 }
5411
5412 static char **JimSaveEnv(char **env)
@@ -7768,10 +7837,11 @@
7837
7838 const char *Jim_String(Jim_Obj *objPtr)
7839 {
7840 if (objPtr->bytes == NULL) {
7841
7842 JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value."));
7843 JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
7844 objPtr->typePtr->updateStringProc(objPtr);
7845 }
7846 return objPtr->bytes;
7847 }
@@ -8530,11 +8600,11 @@
8600 return objPtr;
8601 }
8602
8603 static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
8604 static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
8605 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
8606 static int JimParseCheckMissing(Jim_Interp *interp, int ch);
8607
8608 static const Jim_ObjType scriptObjType = {
8609 "script",
8610 FreeScriptInternalRep,
@@ -8558,10 +8628,11 @@
8628 int inUse; /* Used to share a ScriptObj. Currently
8629 only used by Jim_EvalObj() as protection against
8630 shimmering of the currently evaluated object. */
8631 int firstline;
8632 int linenr;
8633 int missing;
8634 } ScriptObj;
8635
8636 void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
8637 {
8638 int i;
@@ -8836,19 +8907,18 @@
8907 }
8908
8909 script->len = i;
8910 }
8911
8912 static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
8913 {
8914 int scriptTextLen;
8915 const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
8916 struct JimParserCtx parser;
8917 struct ScriptObj *script;
8918 ParseTokenList tokenlist;
8919 int line = 1;
 
8920
8921
8922 if (objPtr->typePtr == &sourceObjType) {
8923 line = objPtr->internalRep.sourceValue.lineNumber;
8924 }
@@ -8861,12 +8931,10 @@
8931 JimParseScript(&parser);
8932 ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
8933 parser.tline);
8934 }
8935
 
 
8936
8937 ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
8938
8939
8940 script = Jim_Alloc(sizeof(*script));
@@ -8876,12 +8944,13 @@
8944 script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
8945 }
8946 else {
8947 script->fileNameObj = interp->emptyObj;
8948 }
 
8949 Jim_IncrRefCount(script->fileNameObj);
8950 script->missing = parser.missing.ch;
8951 script->linenr = parser.missing.line;
8952
8953 ScriptObjAddTokens(interp, script, &tokenlist);
8954
8955
8956 ScriptTokenListFree(&tokenlist);
@@ -8888,28 +8957,37 @@
8957
8958
8959 Jim_FreeIntRep(interp, objPtr);
8960 Jim_SetIntRepPtr(objPtr, script);
8961 objPtr->typePtr = &scriptObjType;
 
 
8962 }
8963
8964 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script);
8965
8966 ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
8967 {
8968 if (objPtr == interp->emptyObj) {
8969
8970 objPtr = interp->nullScriptObj;
8971 }
8972
8973 if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
8974 JimSetScriptFromAny(interp, objPtr);
 
 
8975 }
8976
8977 return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
8978 }
8979
8980 static int JimScriptValid(Jim_Interp *interp, ScriptObj *script)
8981 {
8982 if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
8983 JimAddErrorToStack(interp, script);
8984 return 0;
8985 }
8986 return 1;
8987 }
8988
8989
8990 static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
8991 {
8992 cmdPtr->inUse++;
8993 }
@@ -10901,11 +10979,11 @@
10979 return JIM_OK;
10980 }
10981 else {
10982
10983 if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
10984 Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
10985 return JIM_ERR;
10986 }
10987
10988 Jim_FreeIntRep(interp, objPtr);
10989 }
@@ -12331,10 +12409,11 @@
12409 JIM_EXPROP_UNARYPLUS,
12410
12411
12412 JIM_EXPROP_FUNC_FIRST,
12413 JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
12414 JIM_EXPROP_FUNC_WIDE,
12415 JIM_EXPROP_FUNC_ABS,
12416 JIM_EXPROP_FUNC_DOUBLE,
12417 JIM_EXPROP_FUNC_ROUND,
12418 JIM_EXPROP_FUNC_RAND,
12419 JIM_EXPROP_FUNC_SRAND,
@@ -12397,10 +12476,11 @@
12476 jim_wide wA, wC = 0;
12477
12478 if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
12479 switch (e->opcode) {
12480 case JIM_EXPROP_FUNC_INT:
12481 case JIM_EXPROP_FUNC_WIDE:
12482 case JIM_EXPROP_FUNC_ROUND:
12483 case JIM_EXPROP_UNARYPLUS:
12484 wC = wA;
12485 break;
12486 case JIM_EXPROP_FUNC_DOUBLE:
@@ -12421,10 +12501,11 @@
12501 }
12502 }
12503 else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
12504 switch (e->opcode) {
12505 case JIM_EXPROP_FUNC_INT:
12506 case JIM_EXPROP_FUNC_WIDE:
12507 wC = dA;
12508 break;
12509 case JIM_EXPROP_FUNC_ROUND:
12510 wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
12511 break;
@@ -13093,10 +13174,11 @@
13174 OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
13175
13176
13177
13178 OPRINIT("int", 200, 1, JimExprOpNumUnary),
13179 OPRINIT("wide", 200, 1, JimExprOpNumUnary),
13180 OPRINIT("abs", 200, 1, JimExprOpNumUnary),
13181 OPRINIT("double", 200, 1, JimExprOpNumUnary),
13182 OPRINIT("round", 200, 1, JimExprOpNumUnary),
13183 OPRINIT("rand", 200, 0, JimExprOpNone),
13184 OPRINIT("srand", 200, 1, JimExprOpIntUnary),
@@ -14750,15 +14832,13 @@
14832 ret = Jim_EvalObjVector(interp, objc + 1, nargv);
14833 Jim_Free(nargv);
14834 return ret;
14835 }
14836
14837 static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script)
14838 {
14839 if (!interp->errorFlag) {
 
 
14840
14841 interp->errorFlag = 1;
14842 Jim_IncrRefCount(script->fileNameObj);
14843 Jim_DecrRefCount(interp, interp->errorFileNameObj);
14844 interp->errorFileNameObj = script->fileNameObj;
@@ -14768,11 +14848,11 @@
14848
14849 interp->addStackTrace++;
14850 }
14851
14852
14853 if (interp->addStackTrace > 0) {
14854
14855
14856 JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr);
14857
14858 if (Jim_Length(script->fileNameObj)) {
@@ -14781,16 +14861,10 @@
14861
14862 Jim_DecrRefCount(interp, interp->errorProc);
14863 interp->errorProc = interp->emptyObj;
14864 Jim_IncrRefCount(interp->errorProc);
14865 }
 
 
 
 
 
 
14866 }
14867
14868 static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
14869 {
14870 Jim_Obj *objPtr;
@@ -14928,10 +15002,12 @@
15002
15003 static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
15004 {
15005 int retcode = JIM_OK;
15006
15007 JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
15008
15009 if (listPtr->internalRep.listValue.len) {
15010 Jim_IncrRefCount(listPtr);
15011 retcode = JimInvokeCommand(interp,
15012 listPtr->internalRep.listValue.len,
15013 listPtr->internalRep.listValue.ele);
@@ -14958,12 +15034,12 @@
15034 if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
15035 return JimEvalObjList(interp, scriptObjPtr);
15036 }
15037
15038 Jim_IncrRefCount(scriptObjPtr);
15039 script = JimGetScript(interp, scriptObjPtr);
15040 if (!JimScriptValid(interp, script)) {
15041 Jim_DecrRefCount(interp, scriptObjPtr);
15042 return JIM_ERR;
15043 }
15044
15045 Jim_SetEmptyResult(interp);
@@ -15125,11 +15201,18 @@
15201 argv = sargv;
15202 }
15203 }
15204
15205
15206 if (retcode == JIM_ERR) {
15207 JimAddErrorToStack(interp, script);
15208 }
15209
15210 else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) {
15211
15212 interp->addStackTrace = 0;
15213 }
15214
15215
15216 interp->currentScriptObj = prevScriptObj;
15217
15218 Jim_FreeIntRep(interp, scriptObjPtr);
@@ -15276,11 +15359,11 @@
15359 callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
15360 callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
15361 callFramePtr->staticVars = cmd->u.proc.staticVars;
15362
15363
15364 script = JimGetScript(interp, interp->currentScriptObj);
15365 callFramePtr->fileNameObj = script->fileNameObj;
15366 callFramePtr->line = script->linenr;
15367
15368 Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
15369 Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
@@ -15473,18 +15556,10 @@
15556
15557 scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
15558 JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1);
15559 Jim_IncrRefCount(scriptObjPtr);
15560
 
 
 
 
 
 
 
 
15561 prevScriptObj = interp->currentScriptObj;
15562 interp->currentScriptObj = scriptObjPtr;
15563
15564 retcode = Jim_EvalObj(interp, scriptObjPtr);
15565
@@ -16030,11 +16105,11 @@
16105 Jim_Obj *objPtr;
16106 int cmpOffset;
16107
16108
16109 expr = JimGetExpression(interp, argv[2]);
16110 incrScript = JimGetScript(interp, argv[3]);
16111
16112
16113 if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
16114 goto evalstart;
16115 }
@@ -17017,11 +17092,11 @@
17092 {
17093 Jim_Obj *stringObjPtr;
17094 int i;
17095
17096 if (argc < 2) {
17097 Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
17098 return JIM_ERR;
17099 }
17100 if (argc == 2) {
17101 stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
17102 if (!stringObjPtr)
@@ -17066,11 +17141,11 @@
17141 static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
17142 {
17143 int rc;
17144
17145 if (argc < 2) {
17146 Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
17147 return JIM_ERR;
17148 }
17149
17150 if (argc == 2) {
17151 rc = Jim_EvalObj(interp, argv[1]);
@@ -17277,11 +17352,11 @@
17352 Jim_Obj *cmdList;
17353 Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
17354
17355
17356 cmdList = Jim_DuplicateObj(interp, prefixListObj);
17357 Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
17358
17359 return JimEvalObjList(interp, cmdList);
17360 }
17361
17362 static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
@@ -17597,17 +17672,17 @@
17672 int len;
17673 int opt_case = 1;
17674 int option;
17675 static const char * const options[] = {
17676 "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace",
17677 "map", "repeat", "reverse", "index", "first", "last", "cat",
17678 "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL
17679 };
17680 enum
17681 {
17682 OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE,
17683 OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT,
17684 OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE
17685 };
17686 static const char * const nocase_options[] = {
17687 "-nocase", NULL
17688 };
@@ -17636,10 +17711,29 @@
17711 else {
17712 len = Jim_Length(argv[2]);
17713 }
17714 Jim_SetResultInt(interp, len);
17715 return JIM_OK;
17716
17717 case OPT_CAT:{
17718 Jim_Obj *objPtr;
17719 if (argc == 3) {
17720
17721 objPtr = argv[2];
17722 }
17723 else {
17724 int i;
17725
17726 objPtr = Jim_NewStringObj(interp, "", 0);
17727
17728 for (i = 2; i < argc; i++) {
17729 Jim_AppendObj(interp, objPtr, argv[i]);
17730 }
17731 }
17732 Jim_SetResult(interp, objPtr);
17733 return JIM_OK;
17734 }
17735
17736 case OPT_COMPARE:
17737 case OPT_EQUAL:
17738 {
17739
@@ -18619,38 +18713,47 @@
18713 case INFO_SCRIPT:
18714 if (argc != 2) {
18715 Jim_WrongNumArgs(interp, 2, argv, "");
18716 return JIM_ERR;
18717 }
18718 Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj);
18719 break;
18720
18721 case INFO_SOURCE:{
18722 jim_wide line;
18723 Jim_Obj *resObjPtr;
18724 Jim_Obj *fileNameObj;
18725
18726 if (argc != 3 && argc != 5) {
18727 Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?");
18728 return JIM_ERR;
18729 }
18730 if (argc == 5) {
18731 if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
18732 return JIM_ERR;
18733 }
18734 resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
18735 JimSetSourceInfo(interp, resObjPtr, argv[3], line);
 
 
18736 }
18737 else {
18738 if (argv[2]->typePtr == &sourceObjType) {
18739 fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj;
18740 line = argv[2]->internalRep.sourceValue.lineNumber;
18741 }
18742 else if (argv[2]->typePtr == &scriptObjType) {
18743 ScriptObj *script = JimGetScript(interp, argv[2]);
18744 fileNameObj = script->fileNameObj;
18745 line = script->firstline;
18746 }
18747 else {
18748 fileNameObj = interp->emptyObj;
18749 line = 1;
18750 }
18751 resObjPtr = Jim_NewListObj(interp, NULL, 0);
18752 Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
18753 Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
18754 }
 
 
 
18755 Jim_SetResult(interp, resObjPtr);
18756 break;
18757 }
18758
18759 case INFO_STACKTRACE:
@@ -21843,11 +21946,11 @@
21946
21947 if (Jim_InitStaticExtensions(interp) != JIM_OK) {
21948 JimPrintErrorMessage(interp);
21949 }
21950
21951 Jim_SetVariableStrWithStr(interp, "jim::argv0", argv[0]);
21952 Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
21953 retcode = Jim_initjimshInit(interp);
21954
21955 if (argc == 1) {
21956 if (retcode == JIM_ERR) {
21957
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
3434
if {[hascap o]} {
3535
html "<a href='$home/brlist'>Branches</a>\n"
3636
html "<a href='$home/taglist'>Tags</a>\n"
3737
}
3838
if {[hascap r]} {
39
- html "<a href='$home/reportlist'>Tickets</a>\n"
39
+ html "<a href='$home/ticket'>Tickets</a>\n"
4040
}
4141
if {[hascap j]} {
4242
html "<a href='$home/wiki'>Wiki</a>\n"
4343
}
4444
if {[hascap s]} {
4545
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/reportlist'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/black_and_white/header.txt
+++ skins/black_and_white/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/ticket'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
3333
if {[hascap o]} {
3434
html "<a href='$home/brlist'>Branches</a>\n"
3535
html "<a href='$home/taglist'>Tags</a>\n"
3636
}
3737
if {[hascap r]} {
38
- html "<a href='$home/reportlist'>Tickets</a>\n"
38
+ html "<a href='$home/ticket'>Tickets</a>\n"
3939
}
4040
if {[hascap j]} {
4141
html "<a href='$home/wiki'>Wiki</a>\n"
4242
}
4343
if {[hascap s]} {
4444
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
33 if {[hascap o]} {
34 html "<a href='$home/brlist'>Branches</a>\n"
35 html "<a href='$home/taglist'>Tags</a>\n"
36 }
37 if {[hascap r]} {
38 html "<a href='$home/reportlist'>Tickets</a>\n"
39 }
40 if {[hascap j]} {
41 html "<a href='$home/wiki'>Wiki</a>\n"
42 }
43 if {[hascap s]} {
44
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -33,11 +33,11 @@
33 if {[hascap o]} {
34 html "<a href='$home/brlist'>Branches</a>\n"
35 html "<a href='$home/taglist'>Tags</a>\n"
36 }
37 if {[hascap r]} {
38 html "<a href='$home/ticket'>Tickets</a>\n"
39 }
40 if {[hascap j]} {
41 html "<a href='$home/wiki'>Wiki</a>\n"
42 }
43 if {[hascap s]} {
44
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -187,11 +187,11 @@
187187
188188
/* format for the user list table on the user setup page */
189189
table.usetupUserList {
190190
outline-style: double;
191191
outline-width: 1px;
192
- border-color: white;
192
+ outline-color: white;
193193
padding: 10px;
194194
}
195195
196196
/* color for capabilities, inherited by reader */
197197
span.ueditInheritReader {
198198
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -187,11 +187,11 @@
187
188 /* format for the user list table on the user setup page */
189 table.usetupUserList {
190 outline-style: double;
191 outline-width: 1px;
192 border-color: white;
193 padding: 10px;
194 }
195
196 /* color for capabilities, inherited by reader */
197 span.ueditInheritReader {
198
--- skins/eagle/css.txt
+++ skins/eagle/css.txt
@@ -187,11 +187,11 @@
187
188 /* format for the user list table on the user setup page */
189 table.usetupUserList {
190 outline-style: double;
191 outline-width: 1px;
192 outline-color: white;
193 padding: 10px;
194 }
195
196 /* color for capabilities, inherited by reader */
197 span.ueditInheritReader {
198
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114114
if {[hascap o]} {
115115
html "<a href='$home/brlist'>Branches</a>\n"
116116
html "<a href='$home/taglist'>Tags</a>\n"
117117
}
118118
if {[hascap r]} {
119
- html "<a href='$home/reportlist'>Tickets</a>\n"
119
+ html "<a href='$home/ticket'>Tickets</a>\n"
120120
}
121121
if {[hascap j]} {
122122
html "<a href='$home/wiki'>Wiki</a>\n"
123123
}
124124
if {[hascap s]} {
125125
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/reportlist'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/eagle/header.txt
+++ skins/eagle/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/ticket'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114114
if {[hascap o]} {
115115
html "<a href='$home/brlist'>Branches</a>\n"
116116
html "<a href='$home/taglist'>Tags</a>\n"
117117
}
118118
if {[hascap r]} {
119
- html "<a href='$home/reportlist'>Tickets</a>\n"
119
+ html "<a href='$home/ticket'>Tickets</a>\n"
120120
}
121121
if {[hascap j]} {
122122
html "<a href='$home/wiki'>Wiki</a>\n"
123123
}
124124
if {[hascap s]} {
125125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/reportlist'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/enhanced1/header.txt
+++ skins/enhanced1/header.txt
@@ -114,11 +114,11 @@
114 if {[hascap o]} {
115 html "<a href='$home/brlist'>Branches</a>\n"
116 html "<a href='$home/taglist'>Tags</a>\n"
117 }
118 if {[hascap r]} {
119 html "<a href='$home/ticket'>Tickets</a>\n"
120 }
121 if {[hascap j]} {
122 html "<a href='$home/wiki'>Wiki</a>\n"
123 }
124 if {[hascap s]} {
125
--- skins/etienne1/header.txt
+++ skins/etienne1/header.txt
@@ -51,11 +51,11 @@
5151
|| ($pagename eq "home" && $comphome == 0)
5252
|| ($pagename eq "setup" && $compsetup == 0)
5353
|| ($pagename eq "taglist" && $comptag == 0)
5454
|| ($pagename eq "dir" && $compdir == 0)
5555
|| ($pagename eq "timeline" && $comptl == 0)
56
- || ($pagename eq "reportlist" && $comptkt == 0)
56
+ || ($pagename eq "ticket" && $comptkt == 0)
5757
|| ($pagename eq "brlist" && $compbr == 0)
5858
} {
5959
html " class='active' "
6060
}
6161
@@ -73,11 +73,11 @@
7373
if {[hascap o]} {
7474
menulink "brlist" "/brlist" Branches
7575
menulink "taglist" "/taglist" Tags
7676
}
7777
if {[hascap r]} {
78
- menulink "reportlist" "/reportlist" Tickets
78
+ menulink "ticket" "/ticket" Tickets
7979
}
8080
if {[hascap j]} {
8181
menulink "wiki" "/wiki" Wiki
8282
}
8383
if {[hascap s]} {
8484
--- skins/etienne1/header.txt
+++ skins/etienne1/header.txt
@@ -51,11 +51,11 @@
51 || ($pagename eq "home" && $comphome == 0)
52 || ($pagename eq "setup" && $compsetup == 0)
53 || ($pagename eq "taglist" && $comptag == 0)
54 || ($pagename eq "dir" && $compdir == 0)
55 || ($pagename eq "timeline" && $comptl == 0)
56 || ($pagename eq "reportlist" && $comptkt == 0)
57 || ($pagename eq "brlist" && $compbr == 0)
58 } {
59 html " class='active' "
60 }
61
@@ -73,11 +73,11 @@
73 if {[hascap o]} {
74 menulink "brlist" "/brlist" Branches
75 menulink "taglist" "/taglist" Tags
76 }
77 if {[hascap r]} {
78 menulink "reportlist" "/reportlist" Tickets
79 }
80 if {[hascap j]} {
81 menulink "wiki" "/wiki" Wiki
82 }
83 if {[hascap s]} {
84
--- skins/etienne1/header.txt
+++ skins/etienne1/header.txt
@@ -51,11 +51,11 @@
51 || ($pagename eq "home" && $comphome == 0)
52 || ($pagename eq "setup" && $compsetup == 0)
53 || ($pagename eq "taglist" && $comptag == 0)
54 || ($pagename eq "dir" && $compdir == 0)
55 || ($pagename eq "timeline" && $comptl == 0)
56 || ($pagename eq "ticket" && $comptkt == 0)
57 || ($pagename eq "brlist" && $compbr == 0)
58 } {
59 html " class='active' "
60 }
61
@@ -73,11 +73,11 @@
73 if {[hascap o]} {
74 menulink "brlist" "/brlist" Branches
75 menulink "taglist" "/taglist" Tags
76 }
77 if {[hascap r]} {
78 menulink "ticket" "/ticket" Tickets
79 }
80 if {[hascap j]} {
81 menulink "wiki" "/wiki" Wiki
82 }
83 if {[hascap s]} {
84
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
3232
if {[hascap o]} {
3333
html "<a href='$home/brlist'>Branches</a>\n"
3434
html "<a href='$home/taglist'>Tags</a>\n"
3535
}
3636
if {[hascap r]} {
37
- html "<a href='$home/reportlist'>Tickets</a>\n"
37
+ html "<a href='$home/ticket'>Tickets</a>\n"
3838
}
3939
if {[hascap j]} {
4040
html "<a href='$home/wiki'>Wiki</a>\n"
4141
}
4242
if {[hascap s]} {
4343
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
32 if {[hascap o]} {
33 html "<a href='$home/brlist'>Branches</a>\n"
34 html "<a href='$home/taglist'>Tags</a>\n"
35 }
36 if {[hascap r]} {
37 html "<a href='$home/reportlist'>Tickets</a>\n"
38 }
39 if {[hascap j]} {
40 html "<a href='$home/wiki'>Wiki</a>\n"
41 }
42 if {[hascap s]} {
43
--- skins/khaki/header.txt
+++ skins/khaki/header.txt
@@ -32,11 +32,11 @@
32 if {[hascap o]} {
33 html "<a href='$home/brlist'>Branches</a>\n"
34 html "<a href='$home/taglist'>Tags</a>\n"
35 }
36 if {[hascap r]} {
37 html "<a href='$home/ticket'>Tickets</a>\n"
38 }
39 if {[hascap j]} {
40 html "<a href='$home/wiki'>Wiki</a>\n"
41 }
42 if {[hascap s]} {
43
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
3030
if {[hascap o]} {
3131
html "<a href='$home/brlist'>Branches</a>\n"
3232
html "<a href='$home/taglist'>Tags</a>\n"
3333
}
3434
if {[hascap r]} {
35
- html "<a href='$home/reportlist'>Tickets</a>\n"
35
+ html "<a href='$home/ticket'>Tickets</a>\n"
3636
}
3737
if {[hascap j]} {
3838
html "<a href='$home/wiki'>Wiki</a>\n"
3939
}
4040
if {[hascap s]} {
4141
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
30 if {[hascap o]} {
31 html "<a href='$home/brlist'>Branches</a>\n"
32 html "<a href='$home/taglist'>Tags</a>\n"
33 }
34 if {[hascap r]} {
35 html "<a href='$home/reportlist'>Tickets</a>\n"
36 }
37 if {[hascap j]} {
38 html "<a href='$home/wiki'>Wiki</a>\n"
39 }
40 if {[hascap s]} {
41
--- skins/plain_gray/header.txt
+++ skins/plain_gray/header.txt
@@ -30,11 +30,11 @@
30 if {[hascap o]} {
31 html "<a href='$home/brlist'>Branches</a>\n"
32 html "<a href='$home/taglist'>Tags</a>\n"
33 }
34 if {[hascap r]} {
35 html "<a href='$home/ticket'>Tickets</a>\n"
36 }
37 if {[hascap j]} {
38 html "<a href='$home/wiki'>Wiki</a>\n"
39 }
40 if {[hascap s]} {
41
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
3434
if {[hascap o]} {
3535
html "<a href='$home/brlist'>Branches</a>\n"
3636
html "<a href='$home/taglist'>Tags</a>\n"
3737
}
3838
if {[hascap r]} {
39
- html "<a href='$home/reportlist'>Tickets</a>\n"
39
+ html "<a href='$home/ticket'>Tickets</a>\n"
4040
}
4141
if {[hascap j]} {
4242
html "<a href='$home/wiki'>Wiki</a>\n"
4343
}
4444
if {[hascap s]} {
4545
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/reportlist'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- skins/rounded1/header.txt
+++ skins/rounded1/header.txt
@@ -34,11 +34,11 @@
34 if {[hascap o]} {
35 html "<a href='$home/brlist'>Branches</a>\n"
36 html "<a href='$home/taglist'>Tags</a>\n"
37 }
38 if {[hascap r]} {
39 html "<a href='$home/ticket'>Tickets</a>\n"
40 }
41 if {[hascap j]} {
42 html "<a href='$home/wiki'>Wiki</a>\n"
43 }
44 if {[hascap s]} {
45
--- src/allrepo.c
+++ src/allrepo.c
@@ -229,15 +229,20 @@
229229
collect_argument(&extra, "vacuum",0);
230230
collect_argument(&extra, "deanalyze",0);
231231
collect_argument(&extra, "analyze",0);
232232
collect_argument(&extra, "wal",0);
233233
collect_argument(&extra, "stats",0);
234
+ collect_argument(&extra, "index",0);
235
+ collect_argument(&extra, "no-index",0);
234236
}else if( strncmp(zCmd, "setting", n)==0 ){
235237
zCmd = "setting -R";
236238
collect_argv(&extra, 3);
237239
}else if( strncmp(zCmd, "unset", n)==0 ){
238240
zCmd = "unset -R";
241
+ collect_argv(&extra, 3);
242
+ }else if( strncmp(zCmd, "fts-config", n)==0 ){
243
+ zCmd = "fts-config -R";
239244
collect_argv(&extra, 3);
240245
}else if( strncmp(zCmd, "sync", n)==0 ){
241246
zCmd = "sync -autourl -R";
242247
collect_argument(&extra, "verbose","v");
243248
}else if( strncmp(zCmd, "test-integrity", n)==0 ){
244249
--- src/allrepo.c
+++ src/allrepo.c
@@ -229,15 +229,20 @@
229 collect_argument(&extra, "vacuum",0);
230 collect_argument(&extra, "deanalyze",0);
231 collect_argument(&extra, "analyze",0);
232 collect_argument(&extra, "wal",0);
233 collect_argument(&extra, "stats",0);
 
 
234 }else if( strncmp(zCmd, "setting", n)==0 ){
235 zCmd = "setting -R";
236 collect_argv(&extra, 3);
237 }else if( strncmp(zCmd, "unset", n)==0 ){
238 zCmd = "unset -R";
 
 
 
239 collect_argv(&extra, 3);
240 }else if( strncmp(zCmd, "sync", n)==0 ){
241 zCmd = "sync -autourl -R";
242 collect_argument(&extra, "verbose","v");
243 }else if( strncmp(zCmd, "test-integrity", n)==0 ){
244
--- src/allrepo.c
+++ src/allrepo.c
@@ -229,15 +229,20 @@
229 collect_argument(&extra, "vacuum",0);
230 collect_argument(&extra, "deanalyze",0);
231 collect_argument(&extra, "analyze",0);
232 collect_argument(&extra, "wal",0);
233 collect_argument(&extra, "stats",0);
234 collect_argument(&extra, "index",0);
235 collect_argument(&extra, "no-index",0);
236 }else if( strncmp(zCmd, "setting", n)==0 ){
237 zCmd = "setting -R";
238 collect_argv(&extra, 3);
239 }else if( strncmp(zCmd, "unset", n)==0 ){
240 zCmd = "unset -R";
241 collect_argv(&extra, 3);
242 }else if( strncmp(zCmd, "fts-config", n)==0 ){
243 zCmd = "fts-config -R";
244 collect_argv(&extra, 3);
245 }else if( strncmp(zCmd, "sync", n)==0 ){
246 zCmd = "sync -autourl -R";
247 collect_argument(&extra, "verbose","v");
248 }else if( strncmp(zCmd, "test-integrity", n)==0 ){
249
+1 -1
--- src/clone.c
+++ src/clone.c
@@ -138,11 +138,11 @@
138138
139139
if( g.argc < 4 ){
140140
usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
141141
}
142142
db_open_config(0);
143
- if( file_size(g.argv[3])>0 ){
143
+ if( -1 != file_size(g.argv[3]) ){
144144
fossil_fatal("file already exists: %s", g.argv[3]);
145145
}
146146
147147
url_parse(g.argv[2], urlFlags);
148148
if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
149149
--- src/clone.c
+++ src/clone.c
@@ -138,11 +138,11 @@
138
139 if( g.argc < 4 ){
140 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
141 }
142 db_open_config(0);
143 if( file_size(g.argv[3])>0 ){
144 fossil_fatal("file already exists: %s", g.argv[3]);
145 }
146
147 url_parse(g.argv[2], urlFlags);
148 if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
149
--- src/clone.c
+++ src/clone.c
@@ -138,11 +138,11 @@
138
139 if( g.argc < 4 ){
140 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
141 }
142 db_open_config(0);
143 if( -1 != file_size(g.argv[3]) ){
144 fossil_fatal("file already exists: %s", g.argv[3]);
145 }
146
147 url_parse(g.argv[2], urlFlags);
148 if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user;
149
+3 -3
--- src/configure.c
+++ src/configure.c
@@ -90,11 +90,10 @@
9090
{ "footer", CONFIGSET_SKIN },
9191
{ "logo-mimetype", CONFIGSET_SKIN },
9292
{ "logo-image", CONFIGSET_SKIN },
9393
{ "background-mimetype", CONFIGSET_SKIN },
9494
{ "background-image", CONFIGSET_SKIN },
95
- { "index-page", CONFIGSET_SKIN },
9695
{ "timeline-block-markup", CONFIGSET_SKIN },
9796
{ "timeline-max-comment", CONFIGSET_SKIN },
9897
{ "timeline-plaintext", CONFIGSET_SKIN },
9998
{ "adunit", CONFIGSET_SKIN },
10099
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
@@ -115,10 +114,11 @@
115114
#endif
116115
117116
{ "project-name", CONFIGSET_PROJ },
118117
{ "short-project-name", CONFIGSET_PROJ },
119118
{ "project-description", CONFIGSET_PROJ },
119
+ { "index-page", CONFIGSET_PROJ },
120120
{ "manifest", CONFIGSET_PROJ },
121121
{ "binary-glob", CONFIGSET_PROJ },
122122
{ "clean-glob", CONFIGSET_PROJ },
123123
{ "ignore-glob", CONFIGSET_PROJ },
124124
{ "keep-glob", CONFIGSET_PROJ },
@@ -1061,11 +1061,11 @@
10611061
zTrans[j++] = 'n';
10621062
}else if( c=='\r' ){
10631063
zTrans[j++] = 'r';
10641064
}else if( c=='\t' ){
10651065
zTrans[j++] = 't';
1066
- }else{
1066
+ }else{
10671067
zTrans[j++] = '0' + ((c>>6)&7);
10681068
zTrans[j++] = '0' + ((c>>3)&7);
10691069
zTrans[j++] = '0' + (c&7);
10701070
}
10711071
}
@@ -1098,11 +1098,11 @@
10981098
/*
10991099
** COMMAND: test-var-get
11001100
**
11011101
** Usage: %fossil test-var-get VAR ?FILE?
11021102
**
1103
-** Write the text of the VAR variable into FILE. If FILE is "-"
1103
+** Write the text of the VAR variable into FILE. If FILE is "-"
11041104
** or is omitted then output goes to standard output. VAR can be a
11051105
** GLOB pattern.
11061106
**
11071107
** If not in an open check-out, use the -R REPO option to specify a
11081108
** a repository.
11091109
--- src/configure.c
+++ src/configure.c
@@ -90,11 +90,10 @@
90 { "footer", CONFIGSET_SKIN },
91 { "logo-mimetype", CONFIGSET_SKIN },
92 { "logo-image", CONFIGSET_SKIN },
93 { "background-mimetype", CONFIGSET_SKIN },
94 { "background-image", CONFIGSET_SKIN },
95 { "index-page", CONFIGSET_SKIN },
96 { "timeline-block-markup", CONFIGSET_SKIN },
97 { "timeline-max-comment", CONFIGSET_SKIN },
98 { "timeline-plaintext", CONFIGSET_SKIN },
99 { "adunit", CONFIGSET_SKIN },
100 { "adunit-omit-if-admin", CONFIGSET_SKIN },
@@ -115,10 +114,11 @@
115 #endif
116
117 { "project-name", CONFIGSET_PROJ },
118 { "short-project-name", CONFIGSET_PROJ },
119 { "project-description", CONFIGSET_PROJ },
 
120 { "manifest", CONFIGSET_PROJ },
121 { "binary-glob", CONFIGSET_PROJ },
122 { "clean-glob", CONFIGSET_PROJ },
123 { "ignore-glob", CONFIGSET_PROJ },
124 { "keep-glob", CONFIGSET_PROJ },
@@ -1061,11 +1061,11 @@
1061 zTrans[j++] = 'n';
1062 }else if( c=='\r' ){
1063 zTrans[j++] = 'r';
1064 }else if( c=='\t' ){
1065 zTrans[j++] = 't';
1066 }else{
1067 zTrans[j++] = '0' + ((c>>6)&7);
1068 zTrans[j++] = '0' + ((c>>3)&7);
1069 zTrans[j++] = '0' + (c&7);
1070 }
1071 }
@@ -1098,11 +1098,11 @@
1098 /*
1099 ** COMMAND: test-var-get
1100 **
1101 ** Usage: %fossil test-var-get VAR ?FILE?
1102 **
1103 ** Write the text of the VAR variable into FILE. If FILE is "-"
1104 ** or is omitted then output goes to standard output. VAR can be a
1105 ** GLOB pattern.
1106 **
1107 ** If not in an open check-out, use the -R REPO option to specify a
1108 ** a repository.
1109
--- src/configure.c
+++ src/configure.c
@@ -90,11 +90,10 @@
90 { "footer", CONFIGSET_SKIN },
91 { "logo-mimetype", CONFIGSET_SKIN },
92 { "logo-image", CONFIGSET_SKIN },
93 { "background-mimetype", CONFIGSET_SKIN },
94 { "background-image", CONFIGSET_SKIN },
 
95 { "timeline-block-markup", CONFIGSET_SKIN },
96 { "timeline-max-comment", CONFIGSET_SKIN },
97 { "timeline-plaintext", CONFIGSET_SKIN },
98 { "adunit", CONFIGSET_SKIN },
99 { "adunit-omit-if-admin", CONFIGSET_SKIN },
@@ -115,10 +114,11 @@
114 #endif
115
116 { "project-name", CONFIGSET_PROJ },
117 { "short-project-name", CONFIGSET_PROJ },
118 { "project-description", CONFIGSET_PROJ },
119 { "index-page", CONFIGSET_PROJ },
120 { "manifest", CONFIGSET_PROJ },
121 { "binary-glob", CONFIGSET_PROJ },
122 { "clean-glob", CONFIGSET_PROJ },
123 { "ignore-glob", CONFIGSET_PROJ },
124 { "keep-glob", CONFIGSET_PROJ },
@@ -1061,11 +1061,11 @@
1061 zTrans[j++] = 'n';
1062 }else if( c=='\r' ){
1063 zTrans[j++] = 'r';
1064 }else if( c=='\t' ){
1065 zTrans[j++] = 't';
1066 }else{
1067 zTrans[j++] = '0' + ((c>>6)&7);
1068 zTrans[j++] = '0' + ((c>>3)&7);
1069 zTrans[j++] = '0' + (c&7);
1070 }
1071 }
@@ -1098,11 +1098,11 @@
1098 /*
1099 ** COMMAND: test-var-get
1100 **
1101 ** Usage: %fossil test-var-get VAR ?FILE?
1102 **
1103 ** Write the text of the VAR variable into FILE. If FILE is "-"
1104 ** or is omitted then output goes to standard output. VAR can be a
1105 ** GLOB pattern.
1106 **
1107 ** If not in an open check-out, use the -R REPO option to specify a
1108 ** a repository.
1109
+139 -88
--- src/db.c
+++ src/db.c
@@ -547,12 +547,13 @@
547547
va_end(ap);
548548
z = blob_str(&sql);
549549
while( rc==SQLITE_OK && z[0] ){
550550
pStmt = 0;
551551
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
552
- if( rc!=SQLITE_OK ) break;
553
- if( pStmt ){
552
+ if( rc ){
553
+ db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
554
+ }else if( pStmt ){
554555
db.nPrepare++;
555556
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
556557
rc = sqlite3_finalize(pStmt);
557558
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
558559
}
@@ -1197,11 +1198,11 @@
11971198
*/
11981199
if( !db_table_has_column("repository","mlink","isaux") ){
11991200
db_begin_transaction();
12001201
db_multi_exec(
12011202
"ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;"
1202
- "ALTER TABLE %s.mlink ADD COLUMN isaux INTEGER DEFAULT 0;",
1203
+ "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;",
12031204
db_name("repository"), db_name("repository")
12041205
);
12051206
db_end_transaction(0);
12061207
}
12071208
}
@@ -1380,15 +1381,10 @@
13801381
while( db.pAllStmt ){
13811382
db_finalize(db.pAllStmt);
13821383
}
13831384
db_end_transaction(1);
13841385
pStmt = 0;
1385
- if( reportErrors ){
1386
- while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
1387
- fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
1388
- }
1389
- }
13901386
db_close_config();
13911387
13921388
/* If the localdb (the check-out database) is open and if it has
13931389
** a lot of unused free space, then VACUUM it as we shut down.
13941390
*/
@@ -1399,12 +1395,18 @@
13991395
db_multi_exec("VACUUM;");
14001396
}
14011397
}
14021398
14031399
if( g.db ){
1400
+ int rc;
14041401
sqlite3_wal_checkpoint(g.db, 0);
1405
- sqlite3_close(g.db);
1402
+ rc = sqlite3_close(g.db);
1403
+ if( rc==SQLITE_BUSY && reportErrors ){
1404
+ while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
1405
+ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
1406
+ }
1407
+ }
14061408
g.db = 0;
14071409
g.zMainDbType = 0;
14081410
}
14091411
g.repositoryOpen = 0;
14101412
g.localOpen = 0;
@@ -1520,12 +1522,12 @@
15201522
int i;
15211523
const char *zSep = "";
15221524
15231525
blob_zero(&x);
15241526
blob_append_sql(&x, "(");
1525
- for(i=0; ctrlSettings[i].name; i++){
1526
- blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, ctrlSettings[i].name);
1527
+ for(i=0; aSetting[i].name; i++){
1528
+ blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, aSetting[i].name);
15271529
zSep = ",";
15281530
}
15291531
blob_append_sql(&x, ")");
15301532
return blob_sql_text(&x);
15311533
}
@@ -1682,10 +1684,15 @@
16821684
verify_all_options();
16831685
16841686
if( g.argc!=3 ){
16851687
usage("REPOSITORY-NAME");
16861688
}
1689
+
1690
+ if( -1 != file_size(g.argv[2]) ){
1691
+ fossil_fatal("file already exists: %s", g.argv[2]);
1692
+ }
1693
+
16871694
db_create_repository(g.argv[2]);
16881695
db_open_repository(g.argv[2]);
16891696
db_open_config(0);
16901697
if( zTemplate ) db_attach(zTemplate, "settingSrc");
16911698
db_begin_transaction();
@@ -1913,16 +1920,21 @@
19131920
g.zConfigDbType = zTempDbType;
19141921
}
19151922
}
19161923
19171924
/*
1918
-** Logic for reading potentially versioned settings from
1919
-** .fossil-settings/<name> , and emits warnings if necessary.
1920
-** Returns the non-versioned value without modification if there is no
1921
-** versioned value.
1925
+** Try to read a versioned setting string from .fossil-settings/<name>.
1926
+**
1927
+** Return the text of the string if it is found. Return NULL if not
1928
+** found.
1929
+**
1930
+** If the zNonVersionedSetting parameter is not NULL then it holds the
1931
+** non-versioned value for this setting. If both a versioned and ad
1932
+** non-versioned value exist and are not equal, then a warning message
1933
+** might be generated.
19221934
*/
1923
-char *db_get_do_versionable(const char *zName, char *zNonVersionedSetting){
1935
+char *db_get_versioned(const char *zName, char *zNonVersionedSetting){
19241936
char *zVersionedSetting = 0;
19251937
int noWarn = 0;
19261938
struct _cacheEntry {
19271939
struct _cacheEntry *next;
19281940
const char *zName, *zValue;
@@ -1993,37 +2005,38 @@
19932005
19942006
19952007
/*
19962008
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
19972009
** repository and local databases.
2010
+**
2011
+** If no such variable exists, return zDefault. Or, if zName is the name
2012
+** of a setting, then the zDefault is ignored and the default value of the
2013
+** setting is returned instead. If zName is a versioned setting, then
2014
+** versioned value takes priority.
19982015
*/
19992016
char *db_get(const char *zName, char *zDefault){
20002017
char *z = 0;
2001
- int i;
2002
- const struct stControlSettings *ctrlSetting = 0;
2003
- /* Is this a setting? */
2004
- for(i=0; ctrlSettings[i].name; i++){
2005
- if( strcmp(ctrlSettings[i].name, zName)==0 ){
2006
- ctrlSetting = &(ctrlSettings[i]);
2007
- break;
2008
- }
2009
- }
2018
+ const Setting *pSetting = db_find_setting(zName, 0);
20102019
if( g.repositoryOpen ){
20112020
z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
20122021
}
20132022
if( z==0 && g.zConfigDbName ){
20142023
db_swap_connections();
20152024
z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
20162025
db_swap_connections();
20172026
}
2018
- if( ctrlSetting!=0 && ctrlSetting->versionable ){
2027
+ if( pSetting!=0 && pSetting->versionable ){
20192028
/* This is a versionable setting, try and get the info from a
20202029
** checked out file */
2021
- z = db_get_do_versionable(zName, z);
2030
+ z = db_get_versioned(zName, z);
20222031
}
20232032
if( z==0 ){
2024
- z = zDefault;
2033
+ if( zDefault==0 && pSetting && pSetting->def[0] ){
2034
+ z = fossil_strdup(pSetting->def);
2035
+ }else{
2036
+ z = zDefault;
2037
+ }
20252038
}
20262039
return z;
20272040
}
20282041
char *db_get_mtime(const char *zName, char *zFormat, char *zDefault){
20292042
char *z = 0;
@@ -2285,88 +2298,91 @@
22852298
g.argc = 2;
22862299
info_cmd();
22872300
}
22882301
22892302
/*
2290
-** Print the value of a setting named zName
2303
+** Print the current value of a setting identified by the pSetting
2304
+** pointer.
22912305
*/
2292
-static void print_setting(
2293
- const struct stControlSettings *ctrlSetting,
2294
- int localOpen
2295
-){
2306
+static void print_setting(const Setting *pSetting){
22962307
Stmt q;
22972308
if( g.repositoryOpen ){
22982309
db_prepare(&q,
22992310
"SELECT '(local)', value FROM config WHERE name=%Q"
23002311
" UNION ALL "
23012312
"SELECT '(global)', value FROM global_config WHERE name=%Q",
2302
- ctrlSetting->name, ctrlSetting->name
2313
+ pSetting->name, pSetting->name
23032314
);
23042315
}else{
23052316
db_prepare(&q,
23062317
"SELECT '(global)', value FROM global_config WHERE name=%Q",
2307
- ctrlSetting->name
2318
+ pSetting->name
23082319
);
23092320
}
23102321
if( db_step(&q)==SQLITE_ROW ){
2311
- fossil_print("%-20s %-8s %s\n", ctrlSetting->name, db_column_text(&q, 0),
2322
+ fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0),
23122323
db_column_text(&q, 1));
23132324
}else{
2314
- fossil_print("%-20s\n", ctrlSetting->name);
2325
+ fossil_print("%-20s\n", pSetting->name);
23152326
}
2316
- if( ctrlSetting->versionable && localOpen ){
2327
+ if( pSetting->versionable && g.localOpen ){
23172328
/* Check to see if this is overridden by a versionable settings file */
23182329
Blob versionedPathname;
23192330
blob_zero(&versionedPathname);
23202331
blob_appendf(&versionedPathname, "%s/.fossil-settings/%s",
2321
- g.zLocalRoot, ctrlSetting->name);
2332
+ g.zLocalRoot, pSetting->name);
23222333
if( file_size(blob_str(&versionedPathname))>=0 ){
23232334
fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
2324
- ctrlSetting->name);
2335
+ pSetting->name);
23252336
}
23262337
}
23272338
db_finalize(&q);
23282339
}
23292340
23302341
2342
+#if INTERFACE
23312343
/*
2332
-** define all settings, which can be controlled via the set/unset
2333
-** command. var is the name of the internal configuration name for db_(un)set.
2344
+** Define all settings, which can be controlled via the set/unset
2345
+** command.
2346
+**
2347
+** var is the name of the internal configuration name for db_(un)set.
23342348
** If var is 0, the settings name is used.
2349
+**
23352350
** width is the length for the edit field on the behavior page, 0
23362351
** is used for on/off checkboxes.
2352
+**
23372353
** The behaviour page doesn't use a special layout. It lists all
23382354
** set-commands and displays the 'set'-help as info.
23392355
*/
2340
-#if INTERFACE
2341
-struct stControlSettings {
2356
+struct Setting {
23422357
const char *name; /* Name of the setting */
23432358
const char *var; /* Internal variable name used by db_set() */
23442359
int width; /* Width of display. 0 for boolean values. */
23452360
int versionable; /* Is this setting versionable? */
23462361
int forceTextArea; /* Force using a text area for display? */
23472362
const char *def; /* Default value */
23482363
};
23492364
#endif /* INTERFACE */
2350
-struct stControlSettings const ctrlSettings[] = {
2365
+
2366
+const Setting aSetting[] = {
23512367
{ "access-log", 0, 0, 0, 0, "off" },
23522368
{ "admin-log", 0, 0, 0, 0, "off" },
23532369
{ "allow-symlinks", 0, 0, 1, 0, "off" },
23542370
{ "auto-captcha", "autocaptcha", 0, 0, 0, "on" },
23552371
{ "auto-hyperlink", 0, 0, 0, 0, "on", },
23562372
{ "auto-shun", 0, 0, 0, 0, "on" },
23572373
{ "autosync", 0, 0, 0, 0, "on" },
23582374
{ "autosync-tries", 0, 16, 0, 0, "1" },
23592375
{ "binary-glob", 0, 40, 1, 0, "" },
2360
- { "clearsign", 0, 0, 0, 0, "off" },
23612376
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || \
23622377
defined(__APPLE__)
23632378
{ "case-sensitive", 0, 0, 0, 0, "off" },
23642379
#else
23652380
{ "case-sensitive", 0, 0, 0, 0, "on" },
23662381
#endif
23672382
{ "clean-glob", 0, 40, 1, 0, "" },
2383
+ { "clearsign", 0, 0, 0, 0, "off" },
23682384
{ "crnl-glob", 0, 40, 1, 0, "" },
23692385
{ "default-perms", 0, 16, 0, 0, "u" },
23702386
{ "diff-binary", 0, 0, 0, 0, "on" },
23712387
{ "diff-command", 0, 40, 0, 0, "" },
23722388
{ "dont-push", 0, 0, 0, 0, "off" },
@@ -2407,10 +2423,41 @@
24072423
{ "th1-uri-regexp", 0, 40, 1, 0, "" },
24082424
{ "web-browser", 0, 32, 0, 0, "" },
24092425
{ "white-foreground", 0, 0, 0, 0, "off" },
24102426
{ 0,0,0,0,0,0 }
24112427
};
2428
+
2429
+/*
2430
+** Look up a control setting by its name. Return a pointer to the Setting
2431
+** object, or NULL if there is no such setting.
2432
+**
2433
+** If allowPrefix is true, then the Setting returned is the first one for
2434
+** which zName is a prefix of the Setting name.
2435
+*/
2436
+const Setting *db_find_setting(const char *zName, int allowPrefix){
2437
+ int lwr, mid, upr, c;
2438
+ int n = (int)strlen(zName) + !allowPrefix;
2439
+ lwr = 0;
2440
+ upr = ArraySize(aSetting)-2;
2441
+ while( upr>=lwr ){
2442
+ mid = (upr+lwr)/2;
2443
+ c = fossil_strncmp(zName, aSetting[mid].name, n);
2444
+ if( c<0 ){
2445
+ upr = mid - 1;
2446
+ }else if( c>0 ){
2447
+ lwr = mid + 1;
2448
+ }else{
2449
+ if( allowPrefix ){
2450
+ while( mid>lwr && fossil_strncmp(zName, aSetting[mid-1].name, n)==0 ){
2451
+ mid--;
2452
+ }
2453
+ }
2454
+ return &aSetting[mid];
2455
+ }
2456
+ }
2457
+ return 0;
2458
+}
24122459
24132460
/*
24142461
** COMMAND: settings
24152462
** COMMAND: unset*
24162463
**
@@ -2658,61 +2705,65 @@
26582705
globalFlag = 1;
26592706
}
26602707
if( unsetFlag && g.argc!=3 ){
26612708
usage("PROPERTY ?-global?");
26622709
}
2710
+
2711
+ /* Verify that the aSetting[] entries are in sorted order. This is
2712
+ ** necessary for the binary search in db_find_setting() to work correctly.
2713
+ */
2714
+ for(i=1; aSetting[i].name; i++){
2715
+ if( fossil_strcmp(aSetting[i-1].name, aSetting[i].name)>=0 ){
2716
+ fossil_panic("Internal Error: aSetting[] entries for \"%s\""
2717
+ " and \"%s\" are out of order.",
2718
+ aSetting[i-1].name, aSetting[i].name);
2719
+ }
2720
+ }
2721
+
26632722
if( g.argc==2 ){
2664
- int openLocal = db_open_local(0);
2665
- for(i=0; ctrlSettings[i].name; i++){
2666
- print_setting(&ctrlSettings[i], openLocal);
2723
+ for(i=0; aSetting[i].name; i++){
2724
+ print_setting(&aSetting[i]);
26672725
}
26682726
}else if( g.argc==3 || g.argc==4 ){
26692727
const char *zName = g.argv[2];
2670
- int isManifest;
2671
- int n = strlen(zName);
2672
- for(i=0; ctrlSettings[i].name; i++){
2673
- if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
2674
- }
2675
- if( !ctrlSettings[i].name ){
2728
+ int n = (int)strlen(zName);
2729
+ const Setting *pSetting = db_find_setting(zName, 1);
2730
+ if( pSetting==0 ){
26762731
fossil_fatal("no such setting: %s", zName);
26772732
}
2678
- isManifest = fossil_strcmp(ctrlSettings[i].name, "manifest")==0;
2679
- if( isManifest && globalFlag ){
2733
+ if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
26802734
fossil_fatal("cannot set 'manifest' globally");
26812735
}
26822736
if( unsetFlag || g.argc==4 ){
2683
- if( ctrlSettings[i+1].name
2684
- && strncmp(ctrlSettings[i+1].name, zName, n)==0
2685
- && ctrlSettings[i].name[n]!=0
2686
- ){
2687
- fossil_print("ambiguous property prefix: %s\nMatching properties:\n",
2688
- zName);
2689
- while( ctrlSettings[i].name
2690
- && strncmp(ctrlSettings[i].name, zName, n)==0
2691
- ){
2692
- fossil_print("%s\n", ctrlSettings[i].name);
2693
- i++;
2694
- }
2695
- fossil_exit(1);
2696
- }else{
2697
- if( unsetFlag ){
2698
- db_unset(ctrlSettings[i].name, globalFlag);
2699
- }else{
2700
- db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
2701
- }
2702
- }
2703
- }else{
2704
- isManifest = 0;
2705
- while( ctrlSettings[i].name
2706
- && strncmp(ctrlSettings[i].name, zName, n)==0
2707
- ){
2708
- print_setting(&ctrlSettings[i], db_open_local(0));
2709
- i++;
2710
- }
2711
- }
2712
- if( isManifest && g.localOpen ){
2713
- manifest_to_disk(db_lget_int("checkout", 0));
2737
+ int isManifest = fossil_strcmp(pSetting->name, "manifest")==0;
2738
+ if( pSetting[1].name && fossil_strncmp(pSetting[1].name, zName, n)==0 ){
2739
+ Blob x;
2740
+ int i;
2741
+ blob_init(&x,0,0);
2742
+ for(i=0; pSetting[i].name; i++){
2743
+ if( fossil_strncmp(pSetting[i].name,zName,n)!=0 ) break;
2744
+ blob_appendf(&x, " %s", pSetting[i].name);
2745
+ }
2746
+ fossil_fatal("ambiguous setting \"%s\" - might be:%s",
2747
+ zName, blob_str(&x));
2748
+ }
2749
+ if( globalFlag && isManifest ){
2750
+ fossil_fatal("cannot set 'manifest' globally");
2751
+ }
2752
+ if( unsetFlag ){
2753
+ db_unset(pSetting->name, globalFlag);
2754
+ }else{
2755
+ db_set(pSetting->name, g.argv[3], globalFlag);
2756
+ }
2757
+ if( isManifest && g.localOpen ){
2758
+ manifest_to_disk(db_lget_int("checkout", 0));
2759
+ }
2760
+ }else{
2761
+ while( pSetting->name && fossil_strncmp(pSetting->name,zName,n)==0 ){
2762
+ print_setting(pSetting);
2763
+ pSetting++;
2764
+ }
27142765
}
27152766
}else{
27162767
usage("?PROPERTY? ?VALUE? ?-global?");
27172768
}
27182769
}
27192770
--- src/db.c
+++ src/db.c
@@ -547,12 +547,13 @@
547 va_end(ap);
548 z = blob_str(&sql);
549 while( rc==SQLITE_OK && z[0] ){
550 pStmt = 0;
551 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
552 if( rc!=SQLITE_OK ) break;
553 if( pStmt ){
 
554 db.nPrepare++;
555 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
556 rc = sqlite3_finalize(pStmt);
557 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
558 }
@@ -1197,11 +1198,11 @@
1197 */
1198 if( !db_table_has_column("repository","mlink","isaux") ){
1199 db_begin_transaction();
1200 db_multi_exec(
1201 "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;"
1202 "ALTER TABLE %s.mlink ADD COLUMN isaux INTEGER DEFAULT 0;",
1203 db_name("repository"), db_name("repository")
1204 );
1205 db_end_transaction(0);
1206 }
1207 }
@@ -1380,15 +1381,10 @@
1380 while( db.pAllStmt ){
1381 db_finalize(db.pAllStmt);
1382 }
1383 db_end_transaction(1);
1384 pStmt = 0;
1385 if( reportErrors ){
1386 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
1387 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
1388 }
1389 }
1390 db_close_config();
1391
1392 /* If the localdb (the check-out database) is open and if it has
1393 ** a lot of unused free space, then VACUUM it as we shut down.
1394 */
@@ -1399,12 +1395,18 @@
1399 db_multi_exec("VACUUM;");
1400 }
1401 }
1402
1403 if( g.db ){
 
1404 sqlite3_wal_checkpoint(g.db, 0);
1405 sqlite3_close(g.db);
 
 
 
 
 
1406 g.db = 0;
1407 g.zMainDbType = 0;
1408 }
1409 g.repositoryOpen = 0;
1410 g.localOpen = 0;
@@ -1520,12 +1522,12 @@
1520 int i;
1521 const char *zSep = "";
1522
1523 blob_zero(&x);
1524 blob_append_sql(&x, "(");
1525 for(i=0; ctrlSettings[i].name; i++){
1526 blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, ctrlSettings[i].name);
1527 zSep = ",";
1528 }
1529 blob_append_sql(&x, ")");
1530 return blob_sql_text(&x);
1531 }
@@ -1682,10 +1684,15 @@
1682 verify_all_options();
1683
1684 if( g.argc!=3 ){
1685 usage("REPOSITORY-NAME");
1686 }
 
 
 
 
 
1687 db_create_repository(g.argv[2]);
1688 db_open_repository(g.argv[2]);
1689 db_open_config(0);
1690 if( zTemplate ) db_attach(zTemplate, "settingSrc");
1691 db_begin_transaction();
@@ -1913,16 +1920,21 @@
1913 g.zConfigDbType = zTempDbType;
1914 }
1915 }
1916
1917 /*
1918 ** Logic for reading potentially versioned settings from
1919 ** .fossil-settings/<name> , and emits warnings if necessary.
1920 ** Returns the non-versioned value without modification if there is no
1921 ** versioned value.
 
 
 
 
 
1922 */
1923 char *db_get_do_versionable(const char *zName, char *zNonVersionedSetting){
1924 char *zVersionedSetting = 0;
1925 int noWarn = 0;
1926 struct _cacheEntry {
1927 struct _cacheEntry *next;
1928 const char *zName, *zValue;
@@ -1993,37 +2005,38 @@
1993
1994
1995 /*
1996 ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
1997 ** repository and local databases.
 
 
 
 
 
1998 */
1999 char *db_get(const char *zName, char *zDefault){
2000 char *z = 0;
2001 int i;
2002 const struct stControlSettings *ctrlSetting = 0;
2003 /* Is this a setting? */
2004 for(i=0; ctrlSettings[i].name; i++){
2005 if( strcmp(ctrlSettings[i].name, zName)==0 ){
2006 ctrlSetting = &(ctrlSettings[i]);
2007 break;
2008 }
2009 }
2010 if( g.repositoryOpen ){
2011 z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
2012 }
2013 if( z==0 && g.zConfigDbName ){
2014 db_swap_connections();
2015 z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
2016 db_swap_connections();
2017 }
2018 if( ctrlSetting!=0 && ctrlSetting->versionable ){
2019 /* This is a versionable setting, try and get the info from a
2020 ** checked out file */
2021 z = db_get_do_versionable(zName, z);
2022 }
2023 if( z==0 ){
2024 z = zDefault;
 
 
 
 
2025 }
2026 return z;
2027 }
2028 char *db_get_mtime(const char *zName, char *zFormat, char *zDefault){
2029 char *z = 0;
@@ -2285,88 +2298,91 @@
2285 g.argc = 2;
2286 info_cmd();
2287 }
2288
2289 /*
2290 ** Print the value of a setting named zName
 
2291 */
2292 static void print_setting(
2293 const struct stControlSettings *ctrlSetting,
2294 int localOpen
2295 ){
2296 Stmt q;
2297 if( g.repositoryOpen ){
2298 db_prepare(&q,
2299 "SELECT '(local)', value FROM config WHERE name=%Q"
2300 " UNION ALL "
2301 "SELECT '(global)', value FROM global_config WHERE name=%Q",
2302 ctrlSetting->name, ctrlSetting->name
2303 );
2304 }else{
2305 db_prepare(&q,
2306 "SELECT '(global)', value FROM global_config WHERE name=%Q",
2307 ctrlSetting->name
2308 );
2309 }
2310 if( db_step(&q)==SQLITE_ROW ){
2311 fossil_print("%-20s %-8s %s\n", ctrlSetting->name, db_column_text(&q, 0),
2312 db_column_text(&q, 1));
2313 }else{
2314 fossil_print("%-20s\n", ctrlSetting->name);
2315 }
2316 if( ctrlSetting->versionable && localOpen ){
2317 /* Check to see if this is overridden by a versionable settings file */
2318 Blob versionedPathname;
2319 blob_zero(&versionedPathname);
2320 blob_appendf(&versionedPathname, "%s/.fossil-settings/%s",
2321 g.zLocalRoot, ctrlSetting->name);
2322 if( file_size(blob_str(&versionedPathname))>=0 ){
2323 fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
2324 ctrlSetting->name);
2325 }
2326 }
2327 db_finalize(&q);
2328 }
2329
2330
 
2331 /*
2332 ** define all settings, which can be controlled via the set/unset
2333 ** command. var is the name of the internal configuration name for db_(un)set.
 
 
2334 ** If var is 0, the settings name is used.
 
2335 ** width is the length for the edit field on the behavior page, 0
2336 ** is used for on/off checkboxes.
 
2337 ** The behaviour page doesn't use a special layout. It lists all
2338 ** set-commands and displays the 'set'-help as info.
2339 */
2340 #if INTERFACE
2341 struct stControlSettings {
2342 const char *name; /* Name of the setting */
2343 const char *var; /* Internal variable name used by db_set() */
2344 int width; /* Width of display. 0 for boolean values. */
2345 int versionable; /* Is this setting versionable? */
2346 int forceTextArea; /* Force using a text area for display? */
2347 const char *def; /* Default value */
2348 };
2349 #endif /* INTERFACE */
2350 struct stControlSettings const ctrlSettings[] = {
 
2351 { "access-log", 0, 0, 0, 0, "off" },
2352 { "admin-log", 0, 0, 0, 0, "off" },
2353 { "allow-symlinks", 0, 0, 1, 0, "off" },
2354 { "auto-captcha", "autocaptcha", 0, 0, 0, "on" },
2355 { "auto-hyperlink", 0, 0, 0, 0, "on", },
2356 { "auto-shun", 0, 0, 0, 0, "on" },
2357 { "autosync", 0, 0, 0, 0, "on" },
2358 { "autosync-tries", 0, 16, 0, 0, "1" },
2359 { "binary-glob", 0, 40, 1, 0, "" },
2360 { "clearsign", 0, 0, 0, 0, "off" },
2361 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || \
2362 defined(__APPLE__)
2363 { "case-sensitive", 0, 0, 0, 0, "off" },
2364 #else
2365 { "case-sensitive", 0, 0, 0, 0, "on" },
2366 #endif
2367 { "clean-glob", 0, 40, 1, 0, "" },
 
2368 { "crnl-glob", 0, 40, 1, 0, "" },
2369 { "default-perms", 0, 16, 0, 0, "u" },
2370 { "diff-binary", 0, 0, 0, 0, "on" },
2371 { "diff-command", 0, 40, 0, 0, "" },
2372 { "dont-push", 0, 0, 0, 0, "off" },
@@ -2407,10 +2423,41 @@
2407 { "th1-uri-regexp", 0, 40, 1, 0, "" },
2408 { "web-browser", 0, 32, 0, 0, "" },
2409 { "white-foreground", 0, 0, 0, 0, "off" },
2410 { 0,0,0,0,0,0 }
2411 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2412
2413 /*
2414 ** COMMAND: settings
2415 ** COMMAND: unset*
2416 **
@@ -2658,61 +2705,65 @@
2658 globalFlag = 1;
2659 }
2660 if( unsetFlag && g.argc!=3 ){
2661 usage("PROPERTY ?-global?");
2662 }
 
 
 
 
 
 
 
 
 
 
 
 
2663 if( g.argc==2 ){
2664 int openLocal = db_open_local(0);
2665 for(i=0; ctrlSettings[i].name; i++){
2666 print_setting(&ctrlSettings[i], openLocal);
2667 }
2668 }else if( g.argc==3 || g.argc==4 ){
2669 const char *zName = g.argv[2];
2670 int isManifest;
2671 int n = strlen(zName);
2672 for(i=0; ctrlSettings[i].name; i++){
2673 if( strncmp(ctrlSettings[i].name, zName, n)==0 ) break;
2674 }
2675 if( !ctrlSettings[i].name ){
2676 fossil_fatal("no such setting: %s", zName);
2677 }
2678 isManifest = fossil_strcmp(ctrlSettings[i].name, "manifest")==0;
2679 if( isManifest && globalFlag ){
2680 fossil_fatal("cannot set 'manifest' globally");
2681 }
2682 if( unsetFlag || g.argc==4 ){
2683 if( ctrlSettings[i+1].name
2684 && strncmp(ctrlSettings[i+1].name, zName, n)==0
2685 && ctrlSettings[i].name[n]!=0
2686 ){
2687 fossil_print("ambiguous property prefix: %s\nMatching properties:\n",
2688 zName);
2689 while( ctrlSettings[i].name
2690 && strncmp(ctrlSettings[i].name, zName, n)==0
2691 ){
2692 fossil_print("%s\n", ctrlSettings[i].name);
2693 i++;
2694 }
2695 fossil_exit(1);
2696 }else{
2697 if( unsetFlag ){
2698 db_unset(ctrlSettings[i].name, globalFlag);
2699 }else{
2700 db_set(ctrlSettings[i].name, g.argv[3], globalFlag);
2701 }
2702 }
2703 }else{
2704 isManifest = 0;
2705 while( ctrlSettings[i].name
2706 && strncmp(ctrlSettings[i].name, zName, n)==0
2707 ){
2708 print_setting(&ctrlSettings[i], db_open_local(0));
2709 i++;
2710 }
2711 }
2712 if( isManifest && g.localOpen ){
2713 manifest_to_disk(db_lget_int("checkout", 0));
2714 }
2715 }else{
2716 usage("?PROPERTY? ?VALUE? ?-global?");
2717 }
2718 }
2719
--- src/db.c
+++ src/db.c
@@ -547,12 +547,13 @@
547 va_end(ap);
548 z = blob_str(&sql);
549 while( rc==SQLITE_OK && z[0] ){
550 pStmt = 0;
551 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
552 if( rc ){
553 db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
554 }else if( pStmt ){
555 db.nPrepare++;
556 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
557 rc = sqlite3_finalize(pStmt);
558 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
559 }
@@ -1197,11 +1198,11 @@
1198 */
1199 if( !db_table_has_column("repository","mlink","isaux") ){
1200 db_begin_transaction();
1201 db_multi_exec(
1202 "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;"
1203 "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;",
1204 db_name("repository"), db_name("repository")
1205 );
1206 db_end_transaction(0);
1207 }
1208 }
@@ -1380,15 +1381,10 @@
1381 while( db.pAllStmt ){
1382 db_finalize(db.pAllStmt);
1383 }
1384 db_end_transaction(1);
1385 pStmt = 0;
 
 
 
 
 
1386 db_close_config();
1387
1388 /* If the localdb (the check-out database) is open and if it has
1389 ** a lot of unused free space, then VACUUM it as we shut down.
1390 */
@@ -1399,12 +1395,18 @@
1395 db_multi_exec("VACUUM;");
1396 }
1397 }
1398
1399 if( g.db ){
1400 int rc;
1401 sqlite3_wal_checkpoint(g.db, 0);
1402 rc = sqlite3_close(g.db);
1403 if( rc==SQLITE_BUSY && reportErrors ){
1404 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
1405 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
1406 }
1407 }
1408 g.db = 0;
1409 g.zMainDbType = 0;
1410 }
1411 g.repositoryOpen = 0;
1412 g.localOpen = 0;
@@ -1520,12 +1522,12 @@
1522 int i;
1523 const char *zSep = "";
1524
1525 blob_zero(&x);
1526 blob_append_sql(&x, "(");
1527 for(i=0; aSetting[i].name; i++){
1528 blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, aSetting[i].name);
1529 zSep = ",";
1530 }
1531 blob_append_sql(&x, ")");
1532 return blob_sql_text(&x);
1533 }
@@ -1682,10 +1684,15 @@
1684 verify_all_options();
1685
1686 if( g.argc!=3 ){
1687 usage("REPOSITORY-NAME");
1688 }
1689
1690 if( -1 != file_size(g.argv[2]) ){
1691 fossil_fatal("file already exists: %s", g.argv[2]);
1692 }
1693
1694 db_create_repository(g.argv[2]);
1695 db_open_repository(g.argv[2]);
1696 db_open_config(0);
1697 if( zTemplate ) db_attach(zTemplate, "settingSrc");
1698 db_begin_transaction();
@@ -1913,16 +1920,21 @@
1920 g.zConfigDbType = zTempDbType;
1921 }
1922 }
1923
1924 /*
1925 ** Try to read a versioned setting string from .fossil-settings/<name>.
1926 **
1927 ** Return the text of the string if it is found. Return NULL if not
1928 ** found.
1929 **
1930 ** If the zNonVersionedSetting parameter is not NULL then it holds the
1931 ** non-versioned value for this setting. If both a versioned and ad
1932 ** non-versioned value exist and are not equal, then a warning message
1933 ** might be generated.
1934 */
1935 char *db_get_versioned(const char *zName, char *zNonVersionedSetting){
1936 char *zVersionedSetting = 0;
1937 int noWarn = 0;
1938 struct _cacheEntry {
1939 struct _cacheEntry *next;
1940 const char *zName, *zValue;
@@ -1993,37 +2005,38 @@
2005
2006
2007 /*
2008 ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
2009 ** repository and local databases.
2010 **
2011 ** If no such variable exists, return zDefault. Or, if zName is the name
2012 ** of a setting, then the zDefault is ignored and the default value of the
2013 ** setting is returned instead. If zName is a versioned setting, then
2014 ** versioned value takes priority.
2015 */
2016 char *db_get(const char *zName, char *zDefault){
2017 char *z = 0;
2018 const Setting *pSetting = db_find_setting(zName, 0);
 
 
 
 
 
 
 
 
2019 if( g.repositoryOpen ){
2020 z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName);
2021 }
2022 if( z==0 && g.zConfigDbName ){
2023 db_swap_connections();
2024 z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
2025 db_swap_connections();
2026 }
2027 if( pSetting!=0 && pSetting->versionable ){
2028 /* This is a versionable setting, try and get the info from a
2029 ** checked out file */
2030 z = db_get_versioned(zName, z);
2031 }
2032 if( z==0 ){
2033 if( zDefault==0 && pSetting && pSetting->def[0] ){
2034 z = fossil_strdup(pSetting->def);
2035 }else{
2036 z = zDefault;
2037 }
2038 }
2039 return z;
2040 }
2041 char *db_get_mtime(const char *zName, char *zFormat, char *zDefault){
2042 char *z = 0;
@@ -2285,88 +2298,91 @@
2298 g.argc = 2;
2299 info_cmd();
2300 }
2301
2302 /*
2303 ** Print the current value of a setting identified by the pSetting
2304 ** pointer.
2305 */
2306 static void print_setting(const Setting *pSetting){
 
 
 
2307 Stmt q;
2308 if( g.repositoryOpen ){
2309 db_prepare(&q,
2310 "SELECT '(local)', value FROM config WHERE name=%Q"
2311 " UNION ALL "
2312 "SELECT '(global)', value FROM global_config WHERE name=%Q",
2313 pSetting->name, pSetting->name
2314 );
2315 }else{
2316 db_prepare(&q,
2317 "SELECT '(global)', value FROM global_config WHERE name=%Q",
2318 pSetting->name
2319 );
2320 }
2321 if( db_step(&q)==SQLITE_ROW ){
2322 fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0),
2323 db_column_text(&q, 1));
2324 }else{
2325 fossil_print("%-20s\n", pSetting->name);
2326 }
2327 if( pSetting->versionable && g.localOpen ){
2328 /* Check to see if this is overridden by a versionable settings file */
2329 Blob versionedPathname;
2330 blob_zero(&versionedPathname);
2331 blob_appendf(&versionedPathname, "%s/.fossil-settings/%s",
2332 g.zLocalRoot, pSetting->name);
2333 if( file_size(blob_str(&versionedPathname))>=0 ){
2334 fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
2335 pSetting->name);
2336 }
2337 }
2338 db_finalize(&q);
2339 }
2340
2341
2342 #if INTERFACE
2343 /*
2344 ** Define all settings, which can be controlled via the set/unset
2345 ** command.
2346 **
2347 ** var is the name of the internal configuration name for db_(un)set.
2348 ** If var is 0, the settings name is used.
2349 **
2350 ** width is the length for the edit field on the behavior page, 0
2351 ** is used for on/off checkboxes.
2352 **
2353 ** The behaviour page doesn't use a special layout. It lists all
2354 ** set-commands and displays the 'set'-help as info.
2355 */
2356 struct Setting {
 
2357 const char *name; /* Name of the setting */
2358 const char *var; /* Internal variable name used by db_set() */
2359 int width; /* Width of display. 0 for boolean values. */
2360 int versionable; /* Is this setting versionable? */
2361 int forceTextArea; /* Force using a text area for display? */
2362 const char *def; /* Default value */
2363 };
2364 #endif /* INTERFACE */
2365
2366 const Setting aSetting[] = {
2367 { "access-log", 0, 0, 0, 0, "off" },
2368 { "admin-log", 0, 0, 0, 0, "off" },
2369 { "allow-symlinks", 0, 0, 1, 0, "off" },
2370 { "auto-captcha", "autocaptcha", 0, 0, 0, "on" },
2371 { "auto-hyperlink", 0, 0, 0, 0, "on", },
2372 { "auto-shun", 0, 0, 0, 0, "on" },
2373 { "autosync", 0, 0, 0, 0, "on" },
2374 { "autosync-tries", 0, 16, 0, 0, "1" },
2375 { "binary-glob", 0, 40, 1, 0, "" },
 
2376 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || \
2377 defined(__APPLE__)
2378 { "case-sensitive", 0, 0, 0, 0, "off" },
2379 #else
2380 { "case-sensitive", 0, 0, 0, 0, "on" },
2381 #endif
2382 { "clean-glob", 0, 40, 1, 0, "" },
2383 { "clearsign", 0, 0, 0, 0, "off" },
2384 { "crnl-glob", 0, 40, 1, 0, "" },
2385 { "default-perms", 0, 16, 0, 0, "u" },
2386 { "diff-binary", 0, 0, 0, 0, "on" },
2387 { "diff-command", 0, 40, 0, 0, "" },
2388 { "dont-push", 0, 0, 0, 0, "off" },
@@ -2407,10 +2423,41 @@
2423 { "th1-uri-regexp", 0, 40, 1, 0, "" },
2424 { "web-browser", 0, 32, 0, 0, "" },
2425 { "white-foreground", 0, 0, 0, 0, "off" },
2426 { 0,0,0,0,0,0 }
2427 };
2428
2429 /*
2430 ** Look up a control setting by its name. Return a pointer to the Setting
2431 ** object, or NULL if there is no such setting.
2432 **
2433 ** If allowPrefix is true, then the Setting returned is the first one for
2434 ** which zName is a prefix of the Setting name.
2435 */
2436 const Setting *db_find_setting(const char *zName, int allowPrefix){
2437 int lwr, mid, upr, c;
2438 int n = (int)strlen(zName) + !allowPrefix;
2439 lwr = 0;
2440 upr = ArraySize(aSetting)-2;
2441 while( upr>=lwr ){
2442 mid = (upr+lwr)/2;
2443 c = fossil_strncmp(zName, aSetting[mid].name, n);
2444 if( c<0 ){
2445 upr = mid - 1;
2446 }else if( c>0 ){
2447 lwr = mid + 1;
2448 }else{
2449 if( allowPrefix ){
2450 while( mid>lwr && fossil_strncmp(zName, aSetting[mid-1].name, n)==0 ){
2451 mid--;
2452 }
2453 }
2454 return &aSetting[mid];
2455 }
2456 }
2457 return 0;
2458 }
2459
2460 /*
2461 ** COMMAND: settings
2462 ** COMMAND: unset*
2463 **
@@ -2658,61 +2705,65 @@
2705 globalFlag = 1;
2706 }
2707 if( unsetFlag && g.argc!=3 ){
2708 usage("PROPERTY ?-global?");
2709 }
2710
2711 /* Verify that the aSetting[] entries are in sorted order. This is
2712 ** necessary for the binary search in db_find_setting() to work correctly.
2713 */
2714 for(i=1; aSetting[i].name; i++){
2715 if( fossil_strcmp(aSetting[i-1].name, aSetting[i].name)>=0 ){
2716 fossil_panic("Internal Error: aSetting[] entries for \"%s\""
2717 " and \"%s\" are out of order.",
2718 aSetting[i-1].name, aSetting[i].name);
2719 }
2720 }
2721
2722 if( g.argc==2 ){
2723 for(i=0; aSetting[i].name; i++){
2724 print_setting(&aSetting[i]);
 
2725 }
2726 }else if( g.argc==3 || g.argc==4 ){
2727 const char *zName = g.argv[2];
2728 int n = (int)strlen(zName);
2729 const Setting *pSetting = db_find_setting(zName, 1);
2730 if( pSetting==0 ){
 
 
 
2731 fossil_fatal("no such setting: %s", zName);
2732 }
2733 if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
 
2734 fossil_fatal("cannot set 'manifest' globally");
2735 }
2736 if( unsetFlag || g.argc==4 ){
2737 int isManifest = fossil_strcmp(pSetting->name, "manifest")==0;
2738 if( pSetting[1].name && fossil_strncmp(pSetting[1].name, zName, n)==0 ){
2739 Blob x;
2740 int i;
2741 blob_init(&x,0,0);
2742 for(i=0; pSetting[i].name; i++){
2743 if( fossil_strncmp(pSetting[i].name,zName,n)!=0 ) break;
2744 blob_appendf(&x, " %s", pSetting[i].name);
2745 }
2746 fossil_fatal("ambiguous setting \"%s\" - might be:%s",
2747 zName, blob_str(&x));
2748 }
2749 if( globalFlag && isManifest ){
2750 fossil_fatal("cannot set 'manifest' globally");
2751 }
2752 if( unsetFlag ){
2753 db_unset(pSetting->name, globalFlag);
2754 }else{
2755 db_set(pSetting->name, g.argv[3], globalFlag);
2756 }
2757 if( isManifest && g.localOpen ){
2758 manifest_to_disk(db_lget_int("checkout", 0));
2759 }
2760 }else{
2761 while( pSetting->name && fossil_strncmp(pSetting->name,zName,n)==0 ){
2762 print_setting(pSetting);
2763 pSetting++;
2764 }
 
 
 
2765 }
2766 }else{
2767 usage("?PROPERTY? ?VALUE? ?-global?");
2768 }
2769 }
2770
+424 -289
--- src/doc.c
+++ src/doc.c
@@ -65,17 +65,233 @@
6565
}
6666
}
6767
if( i>=n ){
6868
return 0; /* Plain text */
6969
}
70
- for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
70
+ for(i=0; i<ArraySize(aMime); i++){
7171
if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
7272
return aMime[i].zMimetype;
7373
}
7474
}
7575
return "unknown/unknown";
7676
}
77
+
78
+/* A table of mimetypes based on file suffixes.
79
+** Suffixes must be in sorted order so that we can do a binary
80
+** search to find the mime-type
81
+*/
82
+static const struct {
83
+ const char *zSuffix; /* The file suffix */
84
+ int size; /* Length of the suffix */
85
+ const char *zMimetype; /* The corresponding mimetype */
86
+} aMime[] = {
87
+ { "ai", 2, "application/postscript" },
88
+ { "aif", 3, "audio/x-aiff" },
89
+ { "aifc", 4, "audio/x-aiff" },
90
+ { "aiff", 4, "audio/x-aiff" },
91
+ { "arj", 3, "application/x-arj-compressed" },
92
+ { "asc", 3, "text/plain" },
93
+ { "asf", 3, "video/x-ms-asf" },
94
+ { "asx", 3, "video/x-ms-asx" },
95
+ { "au", 2, "audio/ulaw" },
96
+ { "avi", 3, "video/x-msvideo" },
97
+ { "bat", 3, "application/x-msdos-program" },
98
+ { "bcpio", 5, "application/x-bcpio" },
99
+ { "bin", 3, "application/octet-stream" },
100
+ { "c", 1, "text/plain" },
101
+ { "cc", 2, "text/plain" },
102
+ { "ccad", 4, "application/clariscad" },
103
+ { "cdf", 3, "application/x-netcdf" },
104
+ { "class", 5, "application/octet-stream" },
105
+ { "cod", 3, "application/vnd.rim.cod" },
106
+ { "com", 3, "application/x-msdos-program" },
107
+ { "cpio", 4, "application/x-cpio" },
108
+ { "cpt", 3, "application/mac-compactpro" },
109
+ { "csh", 3, "application/x-csh" },
110
+ { "css", 3, "text/css" },
111
+ { "dcr", 3, "application/x-director" },
112
+ { "deb", 3, "application/x-debian-package" },
113
+ { "dir", 3, "application/x-director" },
114
+ { "dl", 2, "video/dl" },
115
+ { "dms", 3, "application/octet-stream" },
116
+ { "doc", 3, "application/msword" },
117
+ { "docx", 4, "application/vnd.openxmlformats-"
118
+ "officedocument.wordprocessingml.document"},
119
+ { "dot", 3, "application/msword" },
120
+ { "dotx", 4, "application/vnd.openxmlformats-"
121
+ "officedocument.wordprocessingml.template"},
122
+ { "drw", 3, "application/drafting" },
123
+ { "dvi", 3, "application/x-dvi" },
124
+ { "dwg", 3, "application/acad" },
125
+ { "dxf", 3, "application/dxf" },
126
+ { "dxr", 3, "application/x-director" },
127
+ { "eps", 3, "application/postscript" },
128
+ { "etx", 3, "text/x-setext" },
129
+ { "exe", 3, "application/octet-stream" },
130
+ { "ez", 2, "application/andrew-inset" },
131
+ { "f", 1, "text/plain" },
132
+ { "f90", 3, "text/plain" },
133
+ { "fli", 3, "video/fli" },
134
+ { "flv", 3, "video/flv" },
135
+ { "gif", 3, "image/gif" },
136
+ { "gl", 2, "video/gl" },
137
+ { "gtar", 4, "application/x-gtar" },
138
+ { "gz", 2, "application/x-gzip" },
139
+ { "h", 1, "text/plain" },
140
+ { "hdf", 3, "application/x-hdf" },
141
+ { "hh", 2, "text/plain" },
142
+ { "hqx", 3, "application/mac-binhex40" },
143
+ { "htm", 3, "text/html" },
144
+ { "html", 4, "text/html" },
145
+ { "ice", 3, "x-conference/x-cooltalk" },
146
+ { "ief", 3, "image/ief" },
147
+ { "iges", 4, "model/iges" },
148
+ { "igs", 3, "model/iges" },
149
+ { "ips", 3, "application/x-ipscript" },
150
+ { "ipx", 3, "application/x-ipix" },
151
+ { "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
152
+ { "jar", 3, "application/java-archive" },
153
+ { "jpe", 3, "image/jpeg" },
154
+ { "jpeg", 4, "image/jpeg" },
155
+ { "jpg", 3, "image/jpeg" },
156
+ { "js", 2, "application/x-javascript" },
157
+ { "kar", 3, "audio/midi" },
158
+ { "latex", 5, "application/x-latex" },
159
+ { "lha", 3, "application/octet-stream" },
160
+ { "lsp", 3, "application/x-lisp" },
161
+ { "lzh", 3, "application/octet-stream" },
162
+ { "m", 1, "text/plain" },
163
+ { "m3u", 3, "audio/x-mpegurl" },
164
+ { "man", 3, "application/x-troff-man" },
165
+ { "markdown", 8, "text/x-markdown" },
166
+ { "md", 2, "text/x-markdown" },
167
+ { "me", 2, "application/x-troff-me" },
168
+ { "mesh", 4, "model/mesh" },
169
+ { "mid", 3, "audio/midi" },
170
+ { "midi", 4, "audio/midi" },
171
+ { "mif", 3, "application/x-mif" },
172
+ { "mime", 4, "www/mime" },
173
+ { "mkd", 3, "text/x-markdown" },
174
+ { "mov", 3, "video/quicktime" },
175
+ { "movie", 5, "video/x-sgi-movie" },
176
+ { "mp2", 3, "audio/mpeg" },
177
+ { "mp3", 3, "audio/mpeg" },
178
+ { "mp4", 3, "video/mp4" },
179
+ { "mpe", 3, "video/mpeg" },
180
+ { "mpeg", 4, "video/mpeg" },
181
+ { "mpg", 3, "video/mpeg" },
182
+ { "mpga", 4, "audio/mpeg" },
183
+ { "ms", 2, "application/x-troff-ms" },
184
+ { "msh", 3, "model/mesh" },
185
+ { "nc", 2, "application/x-netcdf" },
186
+ { "oda", 3, "application/oda" },
187
+ { "ogg", 3, "application/ogg" },
188
+ { "ogm", 3, "application/ogg" },
189
+ { "pbm", 3, "image/x-portable-bitmap" },
190
+ { "pdb", 3, "chemical/x-pdb" },
191
+ { "pdf", 3, "application/pdf" },
192
+ { "pgm", 3, "image/x-portable-graymap" },
193
+ { "pgn", 3, "application/x-chess-pgn" },
194
+ { "pgp", 3, "application/pgp" },
195
+ { "pl", 2, "application/x-perl" },
196
+ { "pm", 2, "application/x-perl" },
197
+ { "png", 3, "image/png" },
198
+ { "pnm", 3, "image/x-portable-anymap" },
199
+ { "pot", 3, "application/mspowerpoint" },
200
+ { "potx", 4, "application/vnd.openxmlformats-"
201
+ "officedocument.presentationml.template"},
202
+ { "ppm", 3, "image/x-portable-pixmap" },
203
+ { "pps", 3, "application/mspowerpoint" },
204
+ { "ppsx", 4, "application/vnd.openxmlformats-"
205
+ "officedocument.presentationml.slideshow"},
206
+ { "ppt", 3, "application/mspowerpoint" },
207
+ { "pptx", 4, "application/vnd.openxmlformats-"
208
+ "officedocument.presentationml.presentation"},
209
+ { "ppz", 3, "application/mspowerpoint" },
210
+ { "pre", 3, "application/x-freelance" },
211
+ { "prt", 3, "application/pro_eng" },
212
+ { "ps", 2, "application/postscript" },
213
+ { "qt", 2, "video/quicktime" },
214
+ { "ra", 2, "audio/x-realaudio" },
215
+ { "ram", 3, "audio/x-pn-realaudio" },
216
+ { "rar", 3, "application/x-rar-compressed" },
217
+ { "ras", 3, "image/cmu-raster" },
218
+ { "rgb", 3, "image/x-rgb" },
219
+ { "rm", 2, "audio/x-pn-realaudio" },
220
+ { "roff", 4, "application/x-troff" },
221
+ { "rpm", 3, "audio/x-pn-realaudio-plugin" },
222
+ { "rtf", 3, "text/rtf" },
223
+ { "rtx", 3, "text/richtext" },
224
+ { "scm", 3, "application/x-lotusscreencam" },
225
+ { "set", 3, "application/set" },
226
+ { "sgm", 3, "text/sgml" },
227
+ { "sgml", 4, "text/sgml" },
228
+ { "sh", 2, "application/x-sh" },
229
+ { "shar", 4, "application/x-shar" },
230
+ { "silo", 4, "model/mesh" },
231
+ { "sit", 3, "application/x-stuffit" },
232
+ { "skd", 3, "application/x-koan" },
233
+ { "skm", 3, "application/x-koan" },
234
+ { "skp", 3, "application/x-koan" },
235
+ { "skt", 3, "application/x-koan" },
236
+ { "smi", 3, "application/smil" },
237
+ { "smil", 4, "application/smil" },
238
+ { "snd", 3, "audio/basic" },
239
+ { "sol", 3, "application/solids" },
240
+ { "spl", 3, "application/x-futuresplash" },
241
+ { "src", 3, "application/x-wais-source" },
242
+ { "step", 4, "application/STEP" },
243
+ { "stl", 3, "application/SLA" },
244
+ { "stp", 3, "application/STEP" },
245
+ { "sv4cpio", 7, "application/x-sv4cpio" },
246
+ { "sv4crc", 6, "application/x-sv4crc" },
247
+ { "svg", 3, "image/svg+xml" },
248
+ { "swf", 3, "application/x-shockwave-flash" },
249
+ { "t", 1, "application/x-troff" },
250
+ { "tar", 3, "application/x-tar" },
251
+ { "tcl", 3, "application/x-tcl" },
252
+ { "tex", 3, "application/x-tex" },
253
+ { "texi", 4, "application/x-texinfo" },
254
+ { "texinfo", 7, "application/x-texinfo" },
255
+ { "tgz", 3, "application/x-tar-gz" },
256
+ { "th1", 3, "application/x-th1" },
257
+ { "tif", 3, "image/tiff" },
258
+ { "tiff", 4, "image/tiff" },
259
+ { "tr", 2, "application/x-troff" },
260
+ { "tsi", 3, "audio/TSP-audio" },
261
+ { "tsp", 3, "application/dsptype" },
262
+ { "tsv", 3, "text/tab-separated-values" },
263
+ { "txt", 3, "text/plain" },
264
+ { "unv", 3, "application/i-deas" },
265
+ { "ustar", 5, "application/x-ustar" },
266
+ { "vcd", 3, "application/x-cdlink" },
267
+ { "vda", 3, "application/vda" },
268
+ { "viv", 3, "video/vnd.vivo" },
269
+ { "vivo", 4, "video/vnd.vivo" },
270
+ { "vrml", 4, "model/vrml" },
271
+ { "wav", 3, "audio/x-wav" },
272
+ { "wax", 3, "audio/x-ms-wax" },
273
+ { "wiki", 4, "text/x-fossil-wiki" },
274
+ { "wma", 3, "audio/x-ms-wma" },
275
+ { "wmv", 3, "video/x-ms-wmv" },
276
+ { "wmx", 3, "video/x-ms-wmx" },
277
+ { "wrl", 3, "model/vrml" },
278
+ { "wvx", 3, "video/x-ms-wvx" },
279
+ { "xbm", 3, "image/x-xbitmap" },
280
+ { "xlc", 3, "application/vnd.ms-excel" },
281
+ { "xll", 3, "application/vnd.ms-excel" },
282
+ { "xlm", 3, "application/vnd.ms-excel" },
283
+ { "xls", 3, "application/vnd.ms-excel" },
284
+ { "xlsx", 4, "application/vnd.openxmlformats-"
285
+ "officedocument.spreadsheetml.sheet"},
286
+ { "xlw", 3, "application/vnd.ms-excel" },
287
+ { "xml", 3, "text/xml" },
288
+ { "xpm", 3, "image/x-xpixmap" },
289
+ { "xwd", 3, "image/x-xwindowdump" },
290
+ { "xyz", 3, "chemical/x-pdb" },
291
+ { "zip", 3, "application/zip" },
292
+};
77293
78294
/*
79295
** Guess the mime-type of a document based on its name.
80296
*/
81297
const char *mimetype_from_name(const char *zName){
@@ -83,226 +299,17 @@
83299
int i;
84300
int first, last;
85301
int len;
86302
char zSuffix[20];
87303
88
- /* A table of mimetypes based on file suffixes.
89
- ** Suffixes must be in sorted order so that we can do a binary
90
- ** search to find the mime-type
91
- */
92
- static const struct {
93
- const char *zSuffix; /* The file suffix */
94
- int size; /* Length of the suffix */
95
- const char *zMimetype; /* The corresponding mimetype */
96
- } aMime[] = {
97
- { "ai", 2, "application/postscript" },
98
- { "aif", 3, "audio/x-aiff" },
99
- { "aifc", 4, "audio/x-aiff" },
100
- { "aiff", 4, "audio/x-aiff" },
101
- { "arj", 3, "application/x-arj-compressed" },
102
- { "asc", 3, "text/plain" },
103
- { "asf", 3, "video/x-ms-asf" },
104
- { "asx", 3, "video/x-ms-asx" },
105
- { "au", 2, "audio/ulaw" },
106
- { "avi", 3, "video/x-msvideo" },
107
- { "bat", 3, "application/x-msdos-program" },
108
- { "bcpio", 5, "application/x-bcpio" },
109
- { "bin", 3, "application/octet-stream" },
110
- { "c", 1, "text/plain" },
111
- { "cc", 2, "text/plain" },
112
- { "ccad", 4, "application/clariscad" },
113
- { "cdf", 3, "application/x-netcdf" },
114
- { "class", 5, "application/octet-stream" },
115
- { "cod", 3, "application/vnd.rim.cod" },
116
- { "com", 3, "application/x-msdos-program" },
117
- { "cpio", 4, "application/x-cpio" },
118
- { "cpt", 3, "application/mac-compactpro" },
119
- { "csh", 3, "application/x-csh" },
120
- { "css", 3, "text/css" },
121
- { "dcr", 3, "application/x-director" },
122
- { "deb", 3, "application/x-debian-package" },
123
- { "dir", 3, "application/x-director" },
124
- { "dl", 2, "video/dl" },
125
- { "dms", 3, "application/octet-stream" },
126
- { "doc", 3, "application/msword" },
127
- { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
128
- { "dot", 3, "application/msword" },
129
- { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
130
- { "drw", 3, "application/drafting" },
131
- { "dvi", 3, "application/x-dvi" },
132
- { "dwg", 3, "application/acad" },
133
- { "dxf", 3, "application/dxf" },
134
- { "dxr", 3, "application/x-director" },
135
- { "eps", 3, "application/postscript" },
136
- { "etx", 3, "text/x-setext" },
137
- { "exe", 3, "application/octet-stream" },
138
- { "ez", 2, "application/andrew-inset" },
139
- { "f", 1, "text/plain" },
140
- { "f90", 3, "text/plain" },
141
- { "fli", 3, "video/fli" },
142
- { "flv", 3, "video/flv" },
143
- { "gif", 3, "image/gif" },
144
- { "gl", 2, "video/gl" },
145
- { "gtar", 4, "application/x-gtar" },
146
- { "gz", 2, "application/x-gzip" },
147
- { "h", 1, "text/plain" },
148
- { "hdf", 3, "application/x-hdf" },
149
- { "hh", 2, "text/plain" },
150
- { "hqx", 3, "application/mac-binhex40" },
151
- { "htm", 3, "text/html" },
152
- { "html", 4, "text/html" },
153
- { "ice", 3, "x-conference/x-cooltalk" },
154
- { "ief", 3, "image/ief" },
155
- { "iges", 4, "model/iges" },
156
- { "igs", 3, "model/iges" },
157
- { "ips", 3, "application/x-ipscript" },
158
- { "ipx", 3, "application/x-ipix" },
159
- { "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
160
- { "jar", 3, "application/java-archive" },
161
- { "jpe", 3, "image/jpeg" },
162
- { "jpeg", 4, "image/jpeg" },
163
- { "jpg", 3, "image/jpeg" },
164
- { "js", 2, "application/x-javascript" },
165
- { "kar", 3, "audio/midi" },
166
- { "latex", 5, "application/x-latex" },
167
- { "lha", 3, "application/octet-stream" },
168
- { "lsp", 3, "application/x-lisp" },
169
- { "lzh", 3, "application/octet-stream" },
170
- { "m", 1, "text/plain" },
171
- { "m3u", 3, "audio/x-mpegurl" },
172
- { "man", 3, "application/x-troff-man" },
173
- { "markdown", 8, "text/x-markdown" },
174
- { "md", 2, "text/x-markdown" },
175
- { "me", 2, "application/x-troff-me" },
176
- { "mesh", 4, "model/mesh" },
177
- { "mid", 3, "audio/midi" },
178
- { "midi", 4, "audio/midi" },
179
- { "mif", 3, "application/x-mif" },
180
- { "mime", 4, "www/mime" },
181
- { "mkd", 3, "text/x-markdown" },
182
- { "mov", 3, "video/quicktime" },
183
- { "movie", 5, "video/x-sgi-movie" },
184
- { "mp2", 3, "audio/mpeg" },
185
- { "mp3", 3, "audio/mpeg" },
186
- { "mp4", 3, "video/mp4" },
187
- { "mpe", 3, "video/mpeg" },
188
- { "mpeg", 4, "video/mpeg" },
189
- { "mpg", 3, "video/mpeg" },
190
- { "mpga", 4, "audio/mpeg" },
191
- { "ms", 2, "application/x-troff-ms" },
192
- { "msh", 3, "model/mesh" },
193
- { "nc", 2, "application/x-netcdf" },
194
- { "oda", 3, "application/oda" },
195
- { "ogg", 3, "application/ogg" },
196
- { "ogm", 3, "application/ogg" },
197
- { "pbm", 3, "image/x-portable-bitmap" },
198
- { "pdb", 3, "chemical/x-pdb" },
199
- { "pdf", 3, "application/pdf" },
200
- { "pgm", 3, "image/x-portable-graymap" },
201
- { "pgn", 3, "application/x-chess-pgn" },
202
- { "pgp", 3, "application/pgp" },
203
- { "pl", 2, "application/x-perl" },
204
- { "pm", 2, "application/x-perl" },
205
- { "png", 3, "image/png" },
206
- { "pnm", 3, "image/x-portable-anymap" },
207
- { "pot", 3, "application/mspowerpoint" },
208
- { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"},
209
- { "ppm", 3, "image/x-portable-pixmap" },
210
- { "pps", 3, "application/mspowerpoint" },
211
- { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
212
- { "ppt", 3, "application/mspowerpoint" },
213
- { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
214
- { "ppz", 3, "application/mspowerpoint" },
215
- { "pre", 3, "application/x-freelance" },
216
- { "prt", 3, "application/pro_eng" },
217
- { "ps", 2, "application/postscript" },
218
- { "qt", 2, "video/quicktime" },
219
- { "ra", 2, "audio/x-realaudio" },
220
- { "ram", 3, "audio/x-pn-realaudio" },
221
- { "rar", 3, "application/x-rar-compressed" },
222
- { "ras", 3, "image/cmu-raster" },
223
- { "rgb", 3, "image/x-rgb" },
224
- { "rm", 2, "audio/x-pn-realaudio" },
225
- { "roff", 4, "application/x-troff" },
226
- { "rpm", 3, "audio/x-pn-realaudio-plugin" },
227
- { "rtf", 3, "text/rtf" },
228
- { "rtx", 3, "text/richtext" },
229
- { "scm", 3, "application/x-lotusscreencam" },
230
- { "set", 3, "application/set" },
231
- { "sgm", 3, "text/sgml" },
232
- { "sgml", 4, "text/sgml" },
233
- { "sh", 2, "application/x-sh" },
234
- { "shar", 4, "application/x-shar" },
235
- { "silo", 4, "model/mesh" },
236
- { "sit", 3, "application/x-stuffit" },
237
- { "skd", 3, "application/x-koan" },
238
- { "skm", 3, "application/x-koan" },
239
- { "skp", 3, "application/x-koan" },
240
- { "skt", 3, "application/x-koan" },
241
- { "smi", 3, "application/smil" },
242
- { "smil", 4, "application/smil" },
243
- { "snd", 3, "audio/basic" },
244
- { "sol", 3, "application/solids" },
245
- { "spl", 3, "application/x-futuresplash" },
246
- { "src", 3, "application/x-wais-source" },
247
- { "step", 4, "application/STEP" },
248
- { "stl", 3, "application/SLA" },
249
- { "stp", 3, "application/STEP" },
250
- { "sv4cpio", 7, "application/x-sv4cpio" },
251
- { "sv4crc", 6, "application/x-sv4crc" },
252
- { "svg", 3, "image/svg+xml" },
253
- { "swf", 3, "application/x-shockwave-flash" },
254
- { "t", 1, "application/x-troff" },
255
- { "tar", 3, "application/x-tar" },
256
- { "tcl", 3, "application/x-tcl" },
257
- { "tex", 3, "application/x-tex" },
258
- { "texi", 4, "application/x-texinfo" },
259
- { "texinfo", 7, "application/x-texinfo" },
260
- { "tgz", 3, "application/x-tar-gz" },
261
- { "th1", 3, "application/x-th1" },
262
- { "tif", 3, "image/tiff" },
263
- { "tiff", 4, "image/tiff" },
264
- { "tr", 2, "application/x-troff" },
265
- { "tsi", 3, "audio/TSP-audio" },
266
- { "tsp", 3, "application/dsptype" },
267
- { "tsv", 3, "text/tab-separated-values" },
268
- { "txt", 3, "text/plain" },
269
- { "unv", 3, "application/i-deas" },
270
- { "ustar", 5, "application/x-ustar" },
271
- { "vcd", 3, "application/x-cdlink" },
272
- { "vda", 3, "application/vda" },
273
- { "viv", 3, "video/vnd.vivo" },
274
- { "vivo", 4, "video/vnd.vivo" },
275
- { "vrml", 4, "model/vrml" },
276
- { "wav", 3, "audio/x-wav" },
277
- { "wax", 3, "audio/x-ms-wax" },
278
- { "wiki", 4, "text/x-fossil-wiki" },
279
- { "wma", 3, "audio/x-ms-wma" },
280
- { "wmv", 3, "video/x-ms-wmv" },
281
- { "wmx", 3, "video/x-ms-wmx" },
282
- { "wrl", 3, "model/vrml" },
283
- { "wvx", 3, "video/x-ms-wvx" },
284
- { "xbm", 3, "image/x-xbitmap" },
285
- { "xlc", 3, "application/vnd.ms-excel" },
286
- { "xll", 3, "application/vnd.ms-excel" },
287
- { "xlm", 3, "application/vnd.ms-excel" },
288
- { "xls", 3, "application/vnd.ms-excel" },
289
- { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
290
- { "xlw", 3, "application/vnd.ms-excel" },
291
- { "xml", 3, "text/xml" },
292
- { "xpm", 3, "image/x-xpixmap" },
293
- { "xwd", 3, "image/x-xwindowdump" },
294
- { "xyz", 3, "chemical/x-pdb" },
295
- { "zip", 3, "application/zip" },
296
- };
297304
298305
#ifdef FOSSIL_DEBUG
299306
/* This is test code to make sure the table above is in the correct
300307
** order
301308
*/
302309
if( fossil_strcmp(zName, "mimetype-test")==0 ){
303
- for(i=1; i<sizeof(aMime)/sizeof(aMime[0]); i++){
310
+ for(i=1; i<ArraySize(aMime); i++){
304311
if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
305312
fossil_fatal("mimetypes out of sequence: %s before %s",
306313
aMime[i-1].zSuffix, aMime[i].zSuffix);
307314
}
308315
}
@@ -317,11 +324,11 @@
317324
len = strlen(z);
318325
if( len<sizeof(zSuffix)-1 ){
319326
sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
320327
for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
321328
first = 0;
322
- last = sizeof(aMime)/sizeof(aMime[0]) - 1;
329
+ last = ArraySize(aMime) - 1;
323330
while( first<=last ){
324331
int c;
325332
i = (first+last)/2;
326333
c = fossil_strcmp(zSuffix, aMime[i].zSuffix);
327334
if( c==0 ) return aMime[i].zMimetype;
@@ -350,10 +357,134 @@
350357
int i;
351358
for(i=2; i<g.argc; i++){
352359
fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
353360
}
354361
}
362
+
363
+/*
364
+** WEBPAGE: mimetype_list
365
+**
366
+** Show the built-in table used to guess embedded document mimetypes
367
+** from file suffixes.
368
+*/
369
+void mimetype_list_page(void){
370
+ int i;
371
+ style_header("Mimetype List");
372
+ @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
373
+ @ suffixes and the following table to guess at the appropriate mimetype
374
+ @ for each document.</p>
375
+ @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
376
+ @ <thead>
377
+ @ <tr><th>Suffix<th>Mimetype
378
+ @ </thead>
379
+ @ <tbody>
380
+ for(i=0; i<ArraySize(aMime); i++){
381
+ @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
382
+ }
383
+ @ </tbody></table>
384
+ output_table_sorting_javascript("mimeTable","tt",1);
385
+ style_footer();
386
+}
387
+
388
+/*
389
+** Check to see if the file in the pContent blob is "embedded HTML". Return
390
+** true if it is, and fill pTitle with the document title.
391
+**
392
+** An "embedded HTML" file is HTML that lacks a header and a footer. The
393
+** standard Fossil header is prepended and the standard Fossil footer is
394
+** appended. Otherwise, the file is displayed without change.
395
+**
396
+** Embedded HTML must be contained in a <div class='fossil-doc'> element.
397
+** If that <div> also contains a data-title attribute, then the
398
+** value of that attribute is extracted into pTitle and becomes the title
399
+** of the document.
400
+*/
401
+int doc_is_embedded_html(Blob *pContent, Blob *pTitle){
402
+ const char *zIn = blob_str(pContent);
403
+ const char *zAttr;
404
+ const char *zValue;
405
+ int nAttr, nValue;
406
+ int seenClass = 0;
407
+ int seenTitle = 0;
408
+
409
+ while( fossil_isspace(zIn[0]) ) zIn++;
410
+ if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
411
+ zIn += 4;
412
+ while( zIn[0] ){
413
+ if( fossil_isspace(zIn[0]) ) zIn++;
414
+ if( zIn[0]=='>' ) return 0;
415
+ zAttr = zIn;
416
+ while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++;
417
+ nAttr = (int)(zIn - zAttr);
418
+ while( fossil_isspace(zIn[0]) ) zIn++;
419
+ if( zIn[0]!='=' ) continue;
420
+ zIn++;
421
+ while( fossil_isspace(zIn[0]) ) zIn++;
422
+ if( zIn[0]=='"' || zIn[0]=='\'' ){
423
+ char cDelim = zIn[0];
424
+ zIn++;
425
+ zValue = zIn;
426
+ while( zIn[0] && zIn[0]!=cDelim ) zIn++;
427
+ if( zIn[0]==0 ) return 0;
428
+ nValue = (int)(zIn - zValue);
429
+ zIn++;
430
+ }else{
431
+ zValue = zIn;
432
+ while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/'
433
+ && !fossil_isspace(zIn[0]) ) zIn++;
434
+ if( zIn[0]==0 ) return 0;
435
+ nValue = (int)(zIn - zValue);
436
+ }
437
+ if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){
438
+ if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0;
439
+ seenClass = 1;
440
+ if( seenTitle ) return 1;
441
+ }
442
+ if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
443
+ blob_append(pTitle, zValue, nValue);
444
+ seenTitle = 1;
445
+ if( seenClass ) return 1;
446
+ }
447
+ }
448
+ return seenClass;
449
+}
450
+
451
+/*
452
+** Look for a file named zName in the checkin with RID=vid. Load the content
453
+** of that file into pContent and return the RID for the file. Or return 0
454
+** if the file is not found or could not be loaded.
455
+*/
456
+int doc_load_content(int vid, const char *zName, Blob *pContent){
457
+ int rid; /* The RID of the file being loaded */
458
+ if( !db_table_exists("repository","vcache") ){
459
+ db_multi_exec(
460
+ "CREATE TABLE IF NOT EXISTS vcache(\n"
461
+ " vid INTEGER, -- checkin ID\n"
462
+ " fname TEXT, -- filename\n"
463
+ " rid INTEGER, -- artifact ID\n"
464
+ " PRIMARY KEY(vid,fname)\n"
465
+ ") WITHOUT ROWID"
466
+ );
467
+ }
468
+ if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
469
+ db_multi_exec(
470
+ "DELETE FROM vcache;\n"
471
+ "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n"
472
+ "INSERT INTO vcache(vid,fname,rid)"
473
+ " SELECT checkinID, filename, blob.rid FROM foci, blob"
474
+ " WHERE blob.uuid=foci.uuid"
475
+ " AND foci.checkinID=%d;",
476
+ vid
477
+ );
478
+ }
479
+ rid = db_int(0, "SELECT rid FROM vcache"
480
+ " WHERE vid=%d AND fname=%Q", vid, zName);
481
+ if( rid && content_get(rid, pContent)==0 ){
482
+ rid = 0;
483
+ }
484
+ return rid;
485
+}
355486
356487
/*
357488
** WEBPAGE: doc
358489
** URL: /doc?name=CHECKIN/FILE
359490
** URL: /doc/CHECKIN/FILE
@@ -375,97 +506,80 @@
375506
** The "ckout" CHECKIN is intended for development - to provide a mechanism
376507
** for looking at what a file will look like using the /doc webpage after
377508
** it gets checked in.
378509
**
379510
** The file extension is used to decide how to render the file.
511
+**
512
+** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki",
513
+** and "FILE/index.md" are in that order. If none of those are found,
514
+** then FILE is completely replaced by "404.md" and tried. If that is
515
+** not found, then a default 404 screen is generated.
380516
*/
381517
void doc_page(void){
382518
const char *zName; /* Argument to the /doc page */
383
- const char *zOrigName; /* Original document name */
519
+ const char *zOrigName = "?"; /* Original document name */
384520
const char *zMime; /* Document MIME type */
385
- char *zCheckin; /* The checkin holding the document */
521
+ char *zCheckin = "tip"; /* The checkin holding the document */
386522
int vid = 0; /* Artifact of checkin */
387523
int rid = 0; /* Artifact of file */
388524
int i; /* Loop counter */
389525
Blob filebody; /* Content of the documentation file */
390
- int nMiss = 0; /* Failed attempts to find the document */
526
+ Blob title; /* Document title */
527
+ int nMiss = (-1); /* Failed attempts to find the document */
528
+ static const char *const azSuffix[] = {
529
+ "index.html", "index.wiki", "index.md"
530
+ };
391531
392532
login_check_credentials();
393533
if( !g.perm.Read ){ login_needed(); return; }
394
- zName = PD("name", "tip/index.wiki");
395
- for(i=0; zName[i] && zName[i]!='/'; i++){}
396
- zCheckin = mprintf("%.*s", i, zName);
397
- if( zName[i]==0 ){
398
- zName = "index.wiki";
399
- }else{
400
- zName += i;
401
- }
402
- while( zName[0]=='/' ){ zName++; }
403
- g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
404
- zOrigName = zName;
405
- if( !file_is_simple_pathname(zName, 1) ){
406
- if( sqlite3_strglob("*/", zName)==0 ){
407
- zOrigName = zName = mprintf("%sindex.wiki", zName);
408
- if( !file_is_simple_pathname(zName, 1) ){
409
- goto doc_not_found;
410
- }
411
- }else{
412
- goto doc_not_found;
413
- }
414
- }
415
- if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){
416
- sqlite3_snprintf(sizeof(zCheckin), zCheckin, "tip");
417
- }
418
- if( fossil_strcmp(zCheckin,"ckout")==0 ){
419
- /* Read from the local checkout */
420
- char *zFullpath;
421
- db_must_be_within_tree();
422
- while( rid==0 && nMiss<2 ){
423
- zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
424
- if( file_isfile(zFullpath)
425
- && blob_read_from_file(&filebody, zFullpath)<0 ){
534
+ blob_init(&title, 0, 0);
535
+ db_begin_transaction();
536
+ while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
537
+ zName = PD("name", "tip/index.wiki");
538
+ for(i=0; zName[i] && zName[i]!='/'; i++){}
539
+ zCheckin = mprintf("%.*s", i, zName);
540
+ if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){
541
+ zCheckin = "tip";
542
+ }
543
+ if( nMiss==ArraySize(azSuffix) ){
544
+ zName = "404.md";
545
+ }else if( zName[i]==0 ){
546
+ assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
547
+ zName = azSuffix[nMiss];
548
+ }else{
549
+ zName += i;
550
+ }
551
+ while( zName[0]=='/' ){ zName++; }
552
+ g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
553
+ if( nMiss==0 ) zOrigName = zName;
554
+ if( !file_is_simple_pathname(zName, 1) ){
555
+ if( sqlite3_strglob("*/", zName)==0 ){
556
+ assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
557
+ zName = mprintf("%s%s", zName, azSuffix[nMiss]);
558
+ if( !file_is_simple_pathname(zName, 1) ){
559
+ goto doc_not_found;
560
+ }
561
+ }else{
562
+ goto doc_not_found;
563
+ }
564
+ }
565
+ if( fossil_strcmp(zCheckin,"ckout")==0 ){
566
+ /* Read from the local checkout */
567
+ char *zFullpath;
568
+ db_must_be_within_tree();
569
+ zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
570
+ if( file_isfile(zFullpath)
571
+ && blob_read_from_file(&filebody, zFullpath)>0 ){
426572
rid = 1; /* Fake RID just to get the loop to end */
427573
}
428574
fossil_free(zFullpath);
429
- if( rid ) break;
430
- nMiss++;
431
- zName = "404.md";
432
- }
433
- }else{
434
- db_begin_transaction();
435
- vid = name_to_typed_rid(zCheckin, "ci");
436
- db_multi_exec(
437
- "CREATE TABLE IF NOT EXISTS vcache(\n"
438
- " vid INTEGER, -- checkin ID\n"
439
- " fname TEXT, -- filename\n"
440
- " rid INTEGER, -- artifact ID\n"
441
- " PRIMARY KEY(vid,fname)\n"
442
- ") WITHOUT ROWID"
443
- );
444
- if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
445
- db_multi_exec(
446
- "DELETE FROM vcache;\n"
447
- "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n"
448
- "INSERT INTO vcache(vid,fname,rid)"
449
- " SELECT checkinID, filename, blob.rid FROM foci, blob"
450
- " WHERE blob.uuid=foci.uuid"
451
- " AND foci.checkinID=%d;",
452
- vid
453
- );
454
- }
455
- while( rid==0 && nMiss<2 ){
456
- rid = db_int(0, "SELECT rid FROM vcache"
457
- " WHERE vid=%d AND fname=%Q", vid, zName);
458
- if( rid ) break;
459
- nMiss++;
460
- zName = "404.md";
461
- }
462
- if( rid==0 || content_get(rid, &filebody)==0 ){
463
- goto doc_not_found;
464
- }
465
- db_end_transaction(0);
466
- }
575
+ }else{
576
+ vid = name_to_typed_rid(zCheckin, "ci");
577
+ rid = doc_load_content(vid, zName, &filebody);
578
+ }
579
+ }
580
+ if( rid==0 ) goto doc_not_found;
467581
blob_to_utf8_no_bom(&filebody, 0);
468582
469583
/* The file is now contained in the filebody blob. Deliver the
470584
** file to the user
471585
*/
@@ -477,11 +591,11 @@
477591
Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
478592
" FROM blob WHERE rid=%d", vid));
479593
Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
480594
" WHERE objid=%d AND type='ci'", vid));
481595
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
482
- Blob title, tail;
596
+ Blob tail;
483597
style_adunit_config(ADUNIT_RIGHT_OK);
484598
if( wiki_find_title(&filebody, &title, &tail) ){
485599
style_header("%s", blob_str(&title));
486600
wiki_convert(&tail, 0, WIKI_BUTTONS);
487601
}else{
@@ -488,26 +602,32 @@
488602
style_header("Documentation");
489603
wiki_convert(&filebody, 0, WIKI_BUTTONS);
490604
}
491605
style_footer();
492606
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
493
- Blob title = BLOB_INITIALIZER;
494607
Blob tail = BLOB_INITIALIZER;
495608
markdown_to_html(&filebody, &title, &tail);
496609
if( blob_size(&title)>0 ){
497610
style_header("%s", blob_str(&title));
498611
}else{
499
- style_header("%s", nMiss?"Not Found":"Documentation");
612
+ style_header("%s", nMiss>=ArraySize(azSuffix)?
613
+ "Not Found" : "Documentation");
500614
}
501615
blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
502616
style_footer();
503617
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
504618
style_header("Documentation");
505619
@ <blockquote><pre>
506620
@ %h(blob_str(&filebody))
507621
@ </pre></blockquote>
508622
style_footer();
623
+ }else if( fossil_strcmp(zMime, "text/html")==0
624
+ && doc_is_embedded_html(&filebody, &title) ){
625
+ if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
626
+ style_header("%s", blob_str(&title));
627
+ blob_append(cgi_output_blob(), blob_buffer(&filebody),blob_size(&filebody));
628
+ style_footer();
509629
#ifdef FOSSIL_ENABLE_TH1_DOCS
510630
}else if( db_get_boolean("th1-docs", 0) &&
511631
fossil_strcmp(zMime, "application/x-th1")==0 ){
512632
style_header("%h", zName);
513633
Th_Render(blob_str(&filebody));
@@ -515,11 +635,12 @@
515635
#endif
516636
}else{
517637
cgi_set_content_type(zMime);
518638
cgi_set_content(&filebody);
519639
}
520
- if( nMiss ) cgi_set_status(404, "Not Found");
640
+ if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found");
641
+ db_end_transaction(0);
521642
return;
522643
523644
/* Jump here when unable to locate the document */
524645
doc_not_found:
525646
db_end_transaction(0);
@@ -528,10 +649,11 @@
528649
@ <p>Document %h(zOrigName) not found
529650
if( fossil_strcmp(zCheckin,"ckout")!=0 ){
530651
@ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
531652
}
532653
style_footer();
654
+ db_end_transaction(0);
533655
return;
534656
}
535657
536658
/*
537659
** The default logo.
@@ -653,5 +775,18 @@
653775
}
654776
cgi_set_content_type(zMime);
655777
cgi_set_content(&bgimg);
656778
g.isConst = 1;
657779
}
780
+
781
+
782
+/*
783
+** WEBPAGE: /docsrch
784
+**
785
+** Search for documents that match a user-supplied pattern.
786
+*/
787
+void doc_search_page(void){
788
+ login_check_credentials();
789
+ style_header("Document Search");
790
+ search_screen(SRCH_DOC, "docsrch");
791
+ style_footer();
792
+}
658793
--- src/doc.c
+++ src/doc.c
@@ -65,17 +65,233 @@
65 }
66 }
67 if( i>=n ){
68 return 0; /* Plain text */
69 }
70 for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
71 if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
72 return aMime[i].zMimetype;
73 }
74 }
75 return "unknown/unknown";
76 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
78 /*
79 ** Guess the mime-type of a document based on its name.
80 */
81 const char *mimetype_from_name(const char *zName){
@@ -83,226 +299,17 @@
83 int i;
84 int first, last;
85 int len;
86 char zSuffix[20];
87
88 /* A table of mimetypes based on file suffixes.
89 ** Suffixes must be in sorted order so that we can do a binary
90 ** search to find the mime-type
91 */
92 static const struct {
93 const char *zSuffix; /* The file suffix */
94 int size; /* Length of the suffix */
95 const char *zMimetype; /* The corresponding mimetype */
96 } aMime[] = {
97 { "ai", 2, "application/postscript" },
98 { "aif", 3, "audio/x-aiff" },
99 { "aifc", 4, "audio/x-aiff" },
100 { "aiff", 4, "audio/x-aiff" },
101 { "arj", 3, "application/x-arj-compressed" },
102 { "asc", 3, "text/plain" },
103 { "asf", 3, "video/x-ms-asf" },
104 { "asx", 3, "video/x-ms-asx" },
105 { "au", 2, "audio/ulaw" },
106 { "avi", 3, "video/x-msvideo" },
107 { "bat", 3, "application/x-msdos-program" },
108 { "bcpio", 5, "application/x-bcpio" },
109 { "bin", 3, "application/octet-stream" },
110 { "c", 1, "text/plain" },
111 { "cc", 2, "text/plain" },
112 { "ccad", 4, "application/clariscad" },
113 { "cdf", 3, "application/x-netcdf" },
114 { "class", 5, "application/octet-stream" },
115 { "cod", 3, "application/vnd.rim.cod" },
116 { "com", 3, "application/x-msdos-program" },
117 { "cpio", 4, "application/x-cpio" },
118 { "cpt", 3, "application/mac-compactpro" },
119 { "csh", 3, "application/x-csh" },
120 { "css", 3, "text/css" },
121 { "dcr", 3, "application/x-director" },
122 { "deb", 3, "application/x-debian-package" },
123 { "dir", 3, "application/x-director" },
124 { "dl", 2, "video/dl" },
125 { "dms", 3, "application/octet-stream" },
126 { "doc", 3, "application/msword" },
127 { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
128 { "dot", 3, "application/msword" },
129 { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
130 { "drw", 3, "application/drafting" },
131 { "dvi", 3, "application/x-dvi" },
132 { "dwg", 3, "application/acad" },
133 { "dxf", 3, "application/dxf" },
134 { "dxr", 3, "application/x-director" },
135 { "eps", 3, "application/postscript" },
136 { "etx", 3, "text/x-setext" },
137 { "exe", 3, "application/octet-stream" },
138 { "ez", 2, "application/andrew-inset" },
139 { "f", 1, "text/plain" },
140 { "f90", 3, "text/plain" },
141 { "fli", 3, "video/fli" },
142 { "flv", 3, "video/flv" },
143 { "gif", 3, "image/gif" },
144 { "gl", 2, "video/gl" },
145 { "gtar", 4, "application/x-gtar" },
146 { "gz", 2, "application/x-gzip" },
147 { "h", 1, "text/plain" },
148 { "hdf", 3, "application/x-hdf" },
149 { "hh", 2, "text/plain" },
150 { "hqx", 3, "application/mac-binhex40" },
151 { "htm", 3, "text/html" },
152 { "html", 4, "text/html" },
153 { "ice", 3, "x-conference/x-cooltalk" },
154 { "ief", 3, "image/ief" },
155 { "iges", 4, "model/iges" },
156 { "igs", 3, "model/iges" },
157 { "ips", 3, "application/x-ipscript" },
158 { "ipx", 3, "application/x-ipix" },
159 { "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
160 { "jar", 3, "application/java-archive" },
161 { "jpe", 3, "image/jpeg" },
162 { "jpeg", 4, "image/jpeg" },
163 { "jpg", 3, "image/jpeg" },
164 { "js", 2, "application/x-javascript" },
165 { "kar", 3, "audio/midi" },
166 { "latex", 5, "application/x-latex" },
167 { "lha", 3, "application/octet-stream" },
168 { "lsp", 3, "application/x-lisp" },
169 { "lzh", 3, "application/octet-stream" },
170 { "m", 1, "text/plain" },
171 { "m3u", 3, "audio/x-mpegurl" },
172 { "man", 3, "application/x-troff-man" },
173 { "markdown", 8, "text/x-markdown" },
174 { "md", 2, "text/x-markdown" },
175 { "me", 2, "application/x-troff-me" },
176 { "mesh", 4, "model/mesh" },
177 { "mid", 3, "audio/midi" },
178 { "midi", 4, "audio/midi" },
179 { "mif", 3, "application/x-mif" },
180 { "mime", 4, "www/mime" },
181 { "mkd", 3, "text/x-markdown" },
182 { "mov", 3, "video/quicktime" },
183 { "movie", 5, "video/x-sgi-movie" },
184 { "mp2", 3, "audio/mpeg" },
185 { "mp3", 3, "audio/mpeg" },
186 { "mp4", 3, "video/mp4" },
187 { "mpe", 3, "video/mpeg" },
188 { "mpeg", 4, "video/mpeg" },
189 { "mpg", 3, "video/mpeg" },
190 { "mpga", 4, "audio/mpeg" },
191 { "ms", 2, "application/x-troff-ms" },
192 { "msh", 3, "model/mesh" },
193 { "nc", 2, "application/x-netcdf" },
194 { "oda", 3, "application/oda" },
195 { "ogg", 3, "application/ogg" },
196 { "ogm", 3, "application/ogg" },
197 { "pbm", 3, "image/x-portable-bitmap" },
198 { "pdb", 3, "chemical/x-pdb" },
199 { "pdf", 3, "application/pdf" },
200 { "pgm", 3, "image/x-portable-graymap" },
201 { "pgn", 3, "application/x-chess-pgn" },
202 { "pgp", 3, "application/pgp" },
203 { "pl", 2, "application/x-perl" },
204 { "pm", 2, "application/x-perl" },
205 { "png", 3, "image/png" },
206 { "pnm", 3, "image/x-portable-anymap" },
207 { "pot", 3, "application/mspowerpoint" },
208 { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"},
209 { "ppm", 3, "image/x-portable-pixmap" },
210 { "pps", 3, "application/mspowerpoint" },
211 { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
212 { "ppt", 3, "application/mspowerpoint" },
213 { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
214 { "ppz", 3, "application/mspowerpoint" },
215 { "pre", 3, "application/x-freelance" },
216 { "prt", 3, "application/pro_eng" },
217 { "ps", 2, "application/postscript" },
218 { "qt", 2, "video/quicktime" },
219 { "ra", 2, "audio/x-realaudio" },
220 { "ram", 3, "audio/x-pn-realaudio" },
221 { "rar", 3, "application/x-rar-compressed" },
222 { "ras", 3, "image/cmu-raster" },
223 { "rgb", 3, "image/x-rgb" },
224 { "rm", 2, "audio/x-pn-realaudio" },
225 { "roff", 4, "application/x-troff" },
226 { "rpm", 3, "audio/x-pn-realaudio-plugin" },
227 { "rtf", 3, "text/rtf" },
228 { "rtx", 3, "text/richtext" },
229 { "scm", 3, "application/x-lotusscreencam" },
230 { "set", 3, "application/set" },
231 { "sgm", 3, "text/sgml" },
232 { "sgml", 4, "text/sgml" },
233 { "sh", 2, "application/x-sh" },
234 { "shar", 4, "application/x-shar" },
235 { "silo", 4, "model/mesh" },
236 { "sit", 3, "application/x-stuffit" },
237 { "skd", 3, "application/x-koan" },
238 { "skm", 3, "application/x-koan" },
239 { "skp", 3, "application/x-koan" },
240 { "skt", 3, "application/x-koan" },
241 { "smi", 3, "application/smil" },
242 { "smil", 4, "application/smil" },
243 { "snd", 3, "audio/basic" },
244 { "sol", 3, "application/solids" },
245 { "spl", 3, "application/x-futuresplash" },
246 { "src", 3, "application/x-wais-source" },
247 { "step", 4, "application/STEP" },
248 { "stl", 3, "application/SLA" },
249 { "stp", 3, "application/STEP" },
250 { "sv4cpio", 7, "application/x-sv4cpio" },
251 { "sv4crc", 6, "application/x-sv4crc" },
252 { "svg", 3, "image/svg+xml" },
253 { "swf", 3, "application/x-shockwave-flash" },
254 { "t", 1, "application/x-troff" },
255 { "tar", 3, "application/x-tar" },
256 { "tcl", 3, "application/x-tcl" },
257 { "tex", 3, "application/x-tex" },
258 { "texi", 4, "application/x-texinfo" },
259 { "texinfo", 7, "application/x-texinfo" },
260 { "tgz", 3, "application/x-tar-gz" },
261 { "th1", 3, "application/x-th1" },
262 { "tif", 3, "image/tiff" },
263 { "tiff", 4, "image/tiff" },
264 { "tr", 2, "application/x-troff" },
265 { "tsi", 3, "audio/TSP-audio" },
266 { "tsp", 3, "application/dsptype" },
267 { "tsv", 3, "text/tab-separated-values" },
268 { "txt", 3, "text/plain" },
269 { "unv", 3, "application/i-deas" },
270 { "ustar", 5, "application/x-ustar" },
271 { "vcd", 3, "application/x-cdlink" },
272 { "vda", 3, "application/vda" },
273 { "viv", 3, "video/vnd.vivo" },
274 { "vivo", 4, "video/vnd.vivo" },
275 { "vrml", 4, "model/vrml" },
276 { "wav", 3, "audio/x-wav" },
277 { "wax", 3, "audio/x-ms-wax" },
278 { "wiki", 4, "text/x-fossil-wiki" },
279 { "wma", 3, "audio/x-ms-wma" },
280 { "wmv", 3, "video/x-ms-wmv" },
281 { "wmx", 3, "video/x-ms-wmx" },
282 { "wrl", 3, "model/vrml" },
283 { "wvx", 3, "video/x-ms-wvx" },
284 { "xbm", 3, "image/x-xbitmap" },
285 { "xlc", 3, "application/vnd.ms-excel" },
286 { "xll", 3, "application/vnd.ms-excel" },
287 { "xlm", 3, "application/vnd.ms-excel" },
288 { "xls", 3, "application/vnd.ms-excel" },
289 { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
290 { "xlw", 3, "application/vnd.ms-excel" },
291 { "xml", 3, "text/xml" },
292 { "xpm", 3, "image/x-xpixmap" },
293 { "xwd", 3, "image/x-xwindowdump" },
294 { "xyz", 3, "chemical/x-pdb" },
295 { "zip", 3, "application/zip" },
296 };
297
298 #ifdef FOSSIL_DEBUG
299 /* This is test code to make sure the table above is in the correct
300 ** order
301 */
302 if( fossil_strcmp(zName, "mimetype-test")==0 ){
303 for(i=1; i<sizeof(aMime)/sizeof(aMime[0]); i++){
304 if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
305 fossil_fatal("mimetypes out of sequence: %s before %s",
306 aMime[i-1].zSuffix, aMime[i].zSuffix);
307 }
308 }
@@ -317,11 +324,11 @@
317 len = strlen(z);
318 if( len<sizeof(zSuffix)-1 ){
319 sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
320 for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
321 first = 0;
322 last = sizeof(aMime)/sizeof(aMime[0]) - 1;
323 while( first<=last ){
324 int c;
325 i = (first+last)/2;
326 c = fossil_strcmp(zSuffix, aMime[i].zSuffix);
327 if( c==0 ) return aMime[i].zMimetype;
@@ -350,10 +357,134 @@
350 int i;
351 for(i=2; i<g.argc; i++){
352 fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
353 }
354 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
356 /*
357 ** WEBPAGE: doc
358 ** URL: /doc?name=CHECKIN/FILE
359 ** URL: /doc/CHECKIN/FILE
@@ -375,97 +506,80 @@
375 ** The "ckout" CHECKIN is intended for development - to provide a mechanism
376 ** for looking at what a file will look like using the /doc webpage after
377 ** it gets checked in.
378 **
379 ** The file extension is used to decide how to render the file.
 
 
 
 
 
380 */
381 void doc_page(void){
382 const char *zName; /* Argument to the /doc page */
383 const char *zOrigName; /* Original document name */
384 const char *zMime; /* Document MIME type */
385 char *zCheckin; /* The checkin holding the document */
386 int vid = 0; /* Artifact of checkin */
387 int rid = 0; /* Artifact of file */
388 int i; /* Loop counter */
389 Blob filebody; /* Content of the documentation file */
390 int nMiss = 0; /* Failed attempts to find the document */
 
 
 
 
391
392 login_check_credentials();
393 if( !g.perm.Read ){ login_needed(); return; }
394 zName = PD("name", "tip/index.wiki");
395 for(i=0; zName[i] && zName[i]!='/'; i++){}
396 zCheckin = mprintf("%.*s", i, zName);
397 if( zName[i]==0 ){
398 zName = "index.wiki";
399 }else{
400 zName += i;
401 }
402 while( zName[0]=='/' ){ zName++; }
403 g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
404 zOrigName = zName;
405 if( !file_is_simple_pathname(zName, 1) ){
406 if( sqlite3_strglob("*/", zName)==0 ){
407 zOrigName = zName = mprintf("%sindex.wiki", zName);
408 if( !file_is_simple_pathname(zName, 1) ){
409 goto doc_not_found;
410 }
411 }else{
412 goto doc_not_found;
413 }
414 }
415 if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){
416 sqlite3_snprintf(sizeof(zCheckin), zCheckin, "tip");
417 }
418 if( fossil_strcmp(zCheckin,"ckout")==0 ){
419 /* Read from the local checkout */
420 char *zFullpath;
421 db_must_be_within_tree();
422 while( rid==0 && nMiss<2 ){
423 zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
424 if( file_isfile(zFullpath)
425 && blob_read_from_file(&filebody, zFullpath)<0 ){
 
 
 
 
 
 
426 rid = 1; /* Fake RID just to get the loop to end */
427 }
428 fossil_free(zFullpath);
429 if( rid ) break;
430 nMiss++;
431 zName = "404.md";
432 }
433 }else{
434 db_begin_transaction();
435 vid = name_to_typed_rid(zCheckin, "ci");
436 db_multi_exec(
437 "CREATE TABLE IF NOT EXISTS vcache(\n"
438 " vid INTEGER, -- checkin ID\n"
439 " fname TEXT, -- filename\n"
440 " rid INTEGER, -- artifact ID\n"
441 " PRIMARY KEY(vid,fname)\n"
442 ") WITHOUT ROWID"
443 );
444 if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
445 db_multi_exec(
446 "DELETE FROM vcache;\n"
447 "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n"
448 "INSERT INTO vcache(vid,fname,rid)"
449 " SELECT checkinID, filename, blob.rid FROM foci, blob"
450 " WHERE blob.uuid=foci.uuid"
451 " AND foci.checkinID=%d;",
452 vid
453 );
454 }
455 while( rid==0 && nMiss<2 ){
456 rid = db_int(0, "SELECT rid FROM vcache"
457 " WHERE vid=%d AND fname=%Q", vid, zName);
458 if( rid ) break;
459 nMiss++;
460 zName = "404.md";
461 }
462 if( rid==0 || content_get(rid, &filebody)==0 ){
463 goto doc_not_found;
464 }
465 db_end_transaction(0);
466 }
467 blob_to_utf8_no_bom(&filebody, 0);
468
469 /* The file is now contained in the filebody blob. Deliver the
470 ** file to the user
471 */
@@ -477,11 +591,11 @@
477 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
478 " FROM blob WHERE rid=%d", vid));
479 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
480 " WHERE objid=%d AND type='ci'", vid));
481 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
482 Blob title, tail;
483 style_adunit_config(ADUNIT_RIGHT_OK);
484 if( wiki_find_title(&filebody, &title, &tail) ){
485 style_header("%s", blob_str(&title));
486 wiki_convert(&tail, 0, WIKI_BUTTONS);
487 }else{
@@ -488,26 +602,32 @@
488 style_header("Documentation");
489 wiki_convert(&filebody, 0, WIKI_BUTTONS);
490 }
491 style_footer();
492 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
493 Blob title = BLOB_INITIALIZER;
494 Blob tail = BLOB_INITIALIZER;
495 markdown_to_html(&filebody, &title, &tail);
496 if( blob_size(&title)>0 ){
497 style_header("%s", blob_str(&title));
498 }else{
499 style_header("%s", nMiss?"Not Found":"Documentation");
 
500 }
501 blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
502 style_footer();
503 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
504 style_header("Documentation");
505 @ <blockquote><pre>
506 @ %h(blob_str(&filebody))
507 @ </pre></blockquote>
508 style_footer();
 
 
 
 
 
 
509 #ifdef FOSSIL_ENABLE_TH1_DOCS
510 }else if( db_get_boolean("th1-docs", 0) &&
511 fossil_strcmp(zMime, "application/x-th1")==0 ){
512 style_header("%h", zName);
513 Th_Render(blob_str(&filebody));
@@ -515,11 +635,12 @@
515 #endif
516 }else{
517 cgi_set_content_type(zMime);
518 cgi_set_content(&filebody);
519 }
520 if( nMiss ) cgi_set_status(404, "Not Found");
 
521 return;
522
523 /* Jump here when unable to locate the document */
524 doc_not_found:
525 db_end_transaction(0);
@@ -528,10 +649,11 @@
528 @ <p>Document %h(zOrigName) not found
529 if( fossil_strcmp(zCheckin,"ckout")!=0 ){
530 @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
531 }
532 style_footer();
 
533 return;
534 }
535
536 /*
537 ** The default logo.
@@ -653,5 +775,18 @@
653 }
654 cgi_set_content_type(zMime);
655 cgi_set_content(&bgimg);
656 g.isConst = 1;
657 }
 
 
 
 
 
 
 
 
 
 
 
 
 
658
--- src/doc.c
+++ src/doc.c
@@ -65,17 +65,233 @@
65 }
66 }
67 if( i>=n ){
68 return 0; /* Plain text */
69 }
70 for(i=0; i<ArraySize(aMime); i++){
71 if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
72 return aMime[i].zMimetype;
73 }
74 }
75 return "unknown/unknown";
76 }
77
78 /* A table of mimetypes based on file suffixes.
79 ** Suffixes must be in sorted order so that we can do a binary
80 ** search to find the mime-type
81 */
82 static const struct {
83 const char *zSuffix; /* The file suffix */
84 int size; /* Length of the suffix */
85 const char *zMimetype; /* The corresponding mimetype */
86 } aMime[] = {
87 { "ai", 2, "application/postscript" },
88 { "aif", 3, "audio/x-aiff" },
89 { "aifc", 4, "audio/x-aiff" },
90 { "aiff", 4, "audio/x-aiff" },
91 { "arj", 3, "application/x-arj-compressed" },
92 { "asc", 3, "text/plain" },
93 { "asf", 3, "video/x-ms-asf" },
94 { "asx", 3, "video/x-ms-asx" },
95 { "au", 2, "audio/ulaw" },
96 { "avi", 3, "video/x-msvideo" },
97 { "bat", 3, "application/x-msdos-program" },
98 { "bcpio", 5, "application/x-bcpio" },
99 { "bin", 3, "application/octet-stream" },
100 { "c", 1, "text/plain" },
101 { "cc", 2, "text/plain" },
102 { "ccad", 4, "application/clariscad" },
103 { "cdf", 3, "application/x-netcdf" },
104 { "class", 5, "application/octet-stream" },
105 { "cod", 3, "application/vnd.rim.cod" },
106 { "com", 3, "application/x-msdos-program" },
107 { "cpio", 4, "application/x-cpio" },
108 { "cpt", 3, "application/mac-compactpro" },
109 { "csh", 3, "application/x-csh" },
110 { "css", 3, "text/css" },
111 { "dcr", 3, "application/x-director" },
112 { "deb", 3, "application/x-debian-package" },
113 { "dir", 3, "application/x-director" },
114 { "dl", 2, "video/dl" },
115 { "dms", 3, "application/octet-stream" },
116 { "doc", 3, "application/msword" },
117 { "docx", 4, "application/vnd.openxmlformats-"
118 "officedocument.wordprocessingml.document"},
119 { "dot", 3, "application/msword" },
120 { "dotx", 4, "application/vnd.openxmlformats-"
121 "officedocument.wordprocessingml.template"},
122 { "drw", 3, "application/drafting" },
123 { "dvi", 3, "application/x-dvi" },
124 { "dwg", 3, "application/acad" },
125 { "dxf", 3, "application/dxf" },
126 { "dxr", 3, "application/x-director" },
127 { "eps", 3, "application/postscript" },
128 { "etx", 3, "text/x-setext" },
129 { "exe", 3, "application/octet-stream" },
130 { "ez", 2, "application/andrew-inset" },
131 { "f", 1, "text/plain" },
132 { "f90", 3, "text/plain" },
133 { "fli", 3, "video/fli" },
134 { "flv", 3, "video/flv" },
135 { "gif", 3, "image/gif" },
136 { "gl", 2, "video/gl" },
137 { "gtar", 4, "application/x-gtar" },
138 { "gz", 2, "application/x-gzip" },
139 { "h", 1, "text/plain" },
140 { "hdf", 3, "application/x-hdf" },
141 { "hh", 2, "text/plain" },
142 { "hqx", 3, "application/mac-binhex40" },
143 { "htm", 3, "text/html" },
144 { "html", 4, "text/html" },
145 { "ice", 3, "x-conference/x-cooltalk" },
146 { "ief", 3, "image/ief" },
147 { "iges", 4, "model/iges" },
148 { "igs", 3, "model/iges" },
149 { "ips", 3, "application/x-ipscript" },
150 { "ipx", 3, "application/x-ipix" },
151 { "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
152 { "jar", 3, "application/java-archive" },
153 { "jpe", 3, "image/jpeg" },
154 { "jpeg", 4, "image/jpeg" },
155 { "jpg", 3, "image/jpeg" },
156 { "js", 2, "application/x-javascript" },
157 { "kar", 3, "audio/midi" },
158 { "latex", 5, "application/x-latex" },
159 { "lha", 3, "application/octet-stream" },
160 { "lsp", 3, "application/x-lisp" },
161 { "lzh", 3, "application/octet-stream" },
162 { "m", 1, "text/plain" },
163 { "m3u", 3, "audio/x-mpegurl" },
164 { "man", 3, "application/x-troff-man" },
165 { "markdown", 8, "text/x-markdown" },
166 { "md", 2, "text/x-markdown" },
167 { "me", 2, "application/x-troff-me" },
168 { "mesh", 4, "model/mesh" },
169 { "mid", 3, "audio/midi" },
170 { "midi", 4, "audio/midi" },
171 { "mif", 3, "application/x-mif" },
172 { "mime", 4, "www/mime" },
173 { "mkd", 3, "text/x-markdown" },
174 { "mov", 3, "video/quicktime" },
175 { "movie", 5, "video/x-sgi-movie" },
176 { "mp2", 3, "audio/mpeg" },
177 { "mp3", 3, "audio/mpeg" },
178 { "mp4", 3, "video/mp4" },
179 { "mpe", 3, "video/mpeg" },
180 { "mpeg", 4, "video/mpeg" },
181 { "mpg", 3, "video/mpeg" },
182 { "mpga", 4, "audio/mpeg" },
183 { "ms", 2, "application/x-troff-ms" },
184 { "msh", 3, "model/mesh" },
185 { "nc", 2, "application/x-netcdf" },
186 { "oda", 3, "application/oda" },
187 { "ogg", 3, "application/ogg" },
188 { "ogm", 3, "application/ogg" },
189 { "pbm", 3, "image/x-portable-bitmap" },
190 { "pdb", 3, "chemical/x-pdb" },
191 { "pdf", 3, "application/pdf" },
192 { "pgm", 3, "image/x-portable-graymap" },
193 { "pgn", 3, "application/x-chess-pgn" },
194 { "pgp", 3, "application/pgp" },
195 { "pl", 2, "application/x-perl" },
196 { "pm", 2, "application/x-perl" },
197 { "png", 3, "image/png" },
198 { "pnm", 3, "image/x-portable-anymap" },
199 { "pot", 3, "application/mspowerpoint" },
200 { "potx", 4, "application/vnd.openxmlformats-"
201 "officedocument.presentationml.template"},
202 { "ppm", 3, "image/x-portable-pixmap" },
203 { "pps", 3, "application/mspowerpoint" },
204 { "ppsx", 4, "application/vnd.openxmlformats-"
205 "officedocument.presentationml.slideshow"},
206 { "ppt", 3, "application/mspowerpoint" },
207 { "pptx", 4, "application/vnd.openxmlformats-"
208 "officedocument.presentationml.presentation"},
209 { "ppz", 3, "application/mspowerpoint" },
210 { "pre", 3, "application/x-freelance" },
211 { "prt", 3, "application/pro_eng" },
212 { "ps", 2, "application/postscript" },
213 { "qt", 2, "video/quicktime" },
214 { "ra", 2, "audio/x-realaudio" },
215 { "ram", 3, "audio/x-pn-realaudio" },
216 { "rar", 3, "application/x-rar-compressed" },
217 { "ras", 3, "image/cmu-raster" },
218 { "rgb", 3, "image/x-rgb" },
219 { "rm", 2, "audio/x-pn-realaudio" },
220 { "roff", 4, "application/x-troff" },
221 { "rpm", 3, "audio/x-pn-realaudio-plugin" },
222 { "rtf", 3, "text/rtf" },
223 { "rtx", 3, "text/richtext" },
224 { "scm", 3, "application/x-lotusscreencam" },
225 { "set", 3, "application/set" },
226 { "sgm", 3, "text/sgml" },
227 { "sgml", 4, "text/sgml" },
228 { "sh", 2, "application/x-sh" },
229 { "shar", 4, "application/x-shar" },
230 { "silo", 4, "model/mesh" },
231 { "sit", 3, "application/x-stuffit" },
232 { "skd", 3, "application/x-koan" },
233 { "skm", 3, "application/x-koan" },
234 { "skp", 3, "application/x-koan" },
235 { "skt", 3, "application/x-koan" },
236 { "smi", 3, "application/smil" },
237 { "smil", 4, "application/smil" },
238 { "snd", 3, "audio/basic" },
239 { "sol", 3, "application/solids" },
240 { "spl", 3, "application/x-futuresplash" },
241 { "src", 3, "application/x-wais-source" },
242 { "step", 4, "application/STEP" },
243 { "stl", 3, "application/SLA" },
244 { "stp", 3, "application/STEP" },
245 { "sv4cpio", 7, "application/x-sv4cpio" },
246 { "sv4crc", 6, "application/x-sv4crc" },
247 { "svg", 3, "image/svg+xml" },
248 { "swf", 3, "application/x-shockwave-flash" },
249 { "t", 1, "application/x-troff" },
250 { "tar", 3, "application/x-tar" },
251 { "tcl", 3, "application/x-tcl" },
252 { "tex", 3, "application/x-tex" },
253 { "texi", 4, "application/x-texinfo" },
254 { "texinfo", 7, "application/x-texinfo" },
255 { "tgz", 3, "application/x-tar-gz" },
256 { "th1", 3, "application/x-th1" },
257 { "tif", 3, "image/tiff" },
258 { "tiff", 4, "image/tiff" },
259 { "tr", 2, "application/x-troff" },
260 { "tsi", 3, "audio/TSP-audio" },
261 { "tsp", 3, "application/dsptype" },
262 { "tsv", 3, "text/tab-separated-values" },
263 { "txt", 3, "text/plain" },
264 { "unv", 3, "application/i-deas" },
265 { "ustar", 5, "application/x-ustar" },
266 { "vcd", 3, "application/x-cdlink" },
267 { "vda", 3, "application/vda" },
268 { "viv", 3, "video/vnd.vivo" },
269 { "vivo", 4, "video/vnd.vivo" },
270 { "vrml", 4, "model/vrml" },
271 { "wav", 3, "audio/x-wav" },
272 { "wax", 3, "audio/x-ms-wax" },
273 { "wiki", 4, "text/x-fossil-wiki" },
274 { "wma", 3, "audio/x-ms-wma" },
275 { "wmv", 3, "video/x-ms-wmv" },
276 { "wmx", 3, "video/x-ms-wmx" },
277 { "wrl", 3, "model/vrml" },
278 { "wvx", 3, "video/x-ms-wvx" },
279 { "xbm", 3, "image/x-xbitmap" },
280 { "xlc", 3, "application/vnd.ms-excel" },
281 { "xll", 3, "application/vnd.ms-excel" },
282 { "xlm", 3, "application/vnd.ms-excel" },
283 { "xls", 3, "application/vnd.ms-excel" },
284 { "xlsx", 4, "application/vnd.openxmlformats-"
285 "officedocument.spreadsheetml.sheet"},
286 { "xlw", 3, "application/vnd.ms-excel" },
287 { "xml", 3, "text/xml" },
288 { "xpm", 3, "image/x-xpixmap" },
289 { "xwd", 3, "image/x-xwindowdump" },
290 { "xyz", 3, "chemical/x-pdb" },
291 { "zip", 3, "application/zip" },
292 };
293
294 /*
295 ** Guess the mime-type of a document based on its name.
296 */
297 const char *mimetype_from_name(const char *zName){
@@ -83,226 +299,17 @@
299 int i;
300 int first, last;
301 int len;
302 char zSuffix[20];
303
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
305 #ifdef FOSSIL_DEBUG
306 /* This is test code to make sure the table above is in the correct
307 ** order
308 */
309 if( fossil_strcmp(zName, "mimetype-test")==0 ){
310 for(i=1; i<ArraySize(aMime); i++){
311 if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
312 fossil_fatal("mimetypes out of sequence: %s before %s",
313 aMime[i-1].zSuffix, aMime[i].zSuffix);
314 }
315 }
@@ -317,11 +324,11 @@
324 len = strlen(z);
325 if( len<sizeof(zSuffix)-1 ){
326 sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
327 for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
328 first = 0;
329 last = ArraySize(aMime) - 1;
330 while( first<=last ){
331 int c;
332 i = (first+last)/2;
333 c = fossil_strcmp(zSuffix, aMime[i].zSuffix);
334 if( c==0 ) return aMime[i].zMimetype;
@@ -350,10 +357,134 @@
357 int i;
358 for(i=2; i<g.argc; i++){
359 fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
360 }
361 }
362
363 /*
364 ** WEBPAGE: mimetype_list
365 **
366 ** Show the built-in table used to guess embedded document mimetypes
367 ** from file suffixes.
368 */
369 void mimetype_list_page(void){
370 int i;
371 style_header("Mimetype List");
372 @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
373 @ suffixes and the following table to guess at the appropriate mimetype
374 @ for each document.</p>
375 @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
376 @ <thead>
377 @ <tr><th>Suffix<th>Mimetype
378 @ </thead>
379 @ <tbody>
380 for(i=0; i<ArraySize(aMime); i++){
381 @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
382 }
383 @ </tbody></table>
384 output_table_sorting_javascript("mimeTable","tt",1);
385 style_footer();
386 }
387
388 /*
389 ** Check to see if the file in the pContent blob is "embedded HTML". Return
390 ** true if it is, and fill pTitle with the document title.
391 **
392 ** An "embedded HTML" file is HTML that lacks a header and a footer. The
393 ** standard Fossil header is prepended and the standard Fossil footer is
394 ** appended. Otherwise, the file is displayed without change.
395 **
396 ** Embedded HTML must be contained in a <div class='fossil-doc'> element.
397 ** If that <div> also contains a data-title attribute, then the
398 ** value of that attribute is extracted into pTitle and becomes the title
399 ** of the document.
400 */
401 int doc_is_embedded_html(Blob *pContent, Blob *pTitle){
402 const char *zIn = blob_str(pContent);
403 const char *zAttr;
404 const char *zValue;
405 int nAttr, nValue;
406 int seenClass = 0;
407 int seenTitle = 0;
408
409 while( fossil_isspace(zIn[0]) ) zIn++;
410 if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
411 zIn += 4;
412 while( zIn[0] ){
413 if( fossil_isspace(zIn[0]) ) zIn++;
414 if( zIn[0]=='>' ) return 0;
415 zAttr = zIn;
416 while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++;
417 nAttr = (int)(zIn - zAttr);
418 while( fossil_isspace(zIn[0]) ) zIn++;
419 if( zIn[0]!='=' ) continue;
420 zIn++;
421 while( fossil_isspace(zIn[0]) ) zIn++;
422 if( zIn[0]=='"' || zIn[0]=='\'' ){
423 char cDelim = zIn[0];
424 zIn++;
425 zValue = zIn;
426 while( zIn[0] && zIn[0]!=cDelim ) zIn++;
427 if( zIn[0]==0 ) return 0;
428 nValue = (int)(zIn - zValue);
429 zIn++;
430 }else{
431 zValue = zIn;
432 while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/'
433 && !fossil_isspace(zIn[0]) ) zIn++;
434 if( zIn[0]==0 ) return 0;
435 nValue = (int)(zIn - zValue);
436 }
437 if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){
438 if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0;
439 seenClass = 1;
440 if( seenTitle ) return 1;
441 }
442 if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
443 blob_append(pTitle, zValue, nValue);
444 seenTitle = 1;
445 if( seenClass ) return 1;
446 }
447 }
448 return seenClass;
449 }
450
451 /*
452 ** Look for a file named zName in the checkin with RID=vid. Load the content
453 ** of that file into pContent and return the RID for the file. Or return 0
454 ** if the file is not found or could not be loaded.
455 */
456 int doc_load_content(int vid, const char *zName, Blob *pContent){
457 int rid; /* The RID of the file being loaded */
458 if( !db_table_exists("repository","vcache") ){
459 db_multi_exec(
460 "CREATE TABLE IF NOT EXISTS vcache(\n"
461 " vid INTEGER, -- checkin ID\n"
462 " fname TEXT, -- filename\n"
463 " rid INTEGER, -- artifact ID\n"
464 " PRIMARY KEY(vid,fname)\n"
465 ") WITHOUT ROWID"
466 );
467 }
468 if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
469 db_multi_exec(
470 "DELETE FROM vcache;\n"
471 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n"
472 "INSERT INTO vcache(vid,fname,rid)"
473 " SELECT checkinID, filename, blob.rid FROM foci, blob"
474 " WHERE blob.uuid=foci.uuid"
475 " AND foci.checkinID=%d;",
476 vid
477 );
478 }
479 rid = db_int(0, "SELECT rid FROM vcache"
480 " WHERE vid=%d AND fname=%Q", vid, zName);
481 if( rid && content_get(rid, pContent)==0 ){
482 rid = 0;
483 }
484 return rid;
485 }
486
487 /*
488 ** WEBPAGE: doc
489 ** URL: /doc?name=CHECKIN/FILE
490 ** URL: /doc/CHECKIN/FILE
@@ -375,97 +506,80 @@
506 ** The "ckout" CHECKIN is intended for development - to provide a mechanism
507 ** for looking at what a file will look like using the /doc webpage after
508 ** it gets checked in.
509 **
510 ** The file extension is used to decide how to render the file.
511 **
512 ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki",
513 ** and "FILE/index.md" are in that order. If none of those are found,
514 ** then FILE is completely replaced by "404.md" and tried. If that is
515 ** not found, then a default 404 screen is generated.
516 */
517 void doc_page(void){
518 const char *zName; /* Argument to the /doc page */
519 const char *zOrigName = "?"; /* Original document name */
520 const char *zMime; /* Document MIME type */
521 char *zCheckin = "tip"; /* The checkin holding the document */
522 int vid = 0; /* Artifact of checkin */
523 int rid = 0; /* Artifact of file */
524 int i; /* Loop counter */
525 Blob filebody; /* Content of the documentation file */
526 Blob title; /* Document title */
527 int nMiss = (-1); /* Failed attempts to find the document */
528 static const char *const azSuffix[] = {
529 "index.html", "index.wiki", "index.md"
530 };
531
532 login_check_credentials();
533 if( !g.perm.Read ){ login_needed(); return; }
534 blob_init(&title, 0, 0);
535 db_begin_transaction();
536 while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
537 zName = PD("name", "tip/index.wiki");
538 for(i=0; zName[i] && zName[i]!='/'; i++){}
539 zCheckin = mprintf("%.*s", i, zName);
540 if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){
541 zCheckin = "tip";
542 }
543 if( nMiss==ArraySize(azSuffix) ){
544 zName = "404.md";
545 }else if( zName[i]==0 ){
546 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
547 zName = azSuffix[nMiss];
548 }else{
549 zName += i;
550 }
551 while( zName[0]=='/' ){ zName++; }
552 g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName);
553 if( nMiss==0 ) zOrigName = zName;
554 if( !file_is_simple_pathname(zName, 1) ){
555 if( sqlite3_strglob("*/", zName)==0 ){
556 assert( nMiss>=0 && nMiss<ArraySize(azSuffix) );
557 zName = mprintf("%s%s", zName, azSuffix[nMiss]);
558 if( !file_is_simple_pathname(zName, 1) ){
559 goto doc_not_found;
560 }
561 }else{
562 goto doc_not_found;
563 }
564 }
565 if( fossil_strcmp(zCheckin,"ckout")==0 ){
566 /* Read from the local checkout */
567 char *zFullpath;
568 db_must_be_within_tree();
569 zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
570 if( file_isfile(zFullpath)
571 && blob_read_from_file(&filebody, zFullpath)>0 ){
572 rid = 1; /* Fake RID just to get the loop to end */
573 }
574 fossil_free(zFullpath);
575 }else{
576 vid = name_to_typed_rid(zCheckin, "ci");
577 rid = doc_load_content(vid, zName, &filebody);
578 }
579 }
580 if( rid==0 ) goto doc_not_found;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581 blob_to_utf8_no_bom(&filebody, 0);
582
583 /* The file is now contained in the filebody blob. Deliver the
584 ** file to the user
585 */
@@ -477,11 +591,11 @@
591 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
592 " FROM blob WHERE rid=%d", vid));
593 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
594 " WHERE objid=%d AND type='ci'", vid));
595 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
596 Blob tail;
597 style_adunit_config(ADUNIT_RIGHT_OK);
598 if( wiki_find_title(&filebody, &title, &tail) ){
599 style_header("%s", blob_str(&title));
600 wiki_convert(&tail, 0, WIKI_BUTTONS);
601 }else{
@@ -488,26 +602,32 @@
602 style_header("Documentation");
603 wiki_convert(&filebody, 0, WIKI_BUTTONS);
604 }
605 style_footer();
606 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
 
607 Blob tail = BLOB_INITIALIZER;
608 markdown_to_html(&filebody, &title, &tail);
609 if( blob_size(&title)>0 ){
610 style_header("%s", blob_str(&title));
611 }else{
612 style_header("%s", nMiss>=ArraySize(azSuffix)?
613 "Not Found" : "Documentation");
614 }
615 blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
616 style_footer();
617 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
618 style_header("Documentation");
619 @ <blockquote><pre>
620 @ %h(blob_str(&filebody))
621 @ </pre></blockquote>
622 style_footer();
623 }else if( fossil_strcmp(zMime, "text/html")==0
624 && doc_is_embedded_html(&filebody, &title) ){
625 if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
626 style_header("%s", blob_str(&title));
627 blob_append(cgi_output_blob(), blob_buffer(&filebody),blob_size(&filebody));
628 style_footer();
629 #ifdef FOSSIL_ENABLE_TH1_DOCS
630 }else if( db_get_boolean("th1-docs", 0) &&
631 fossil_strcmp(zMime, "application/x-th1")==0 ){
632 style_header("%h", zName);
633 Th_Render(blob_str(&filebody));
@@ -515,11 +635,12 @@
635 #endif
636 }else{
637 cgi_set_content_type(zMime);
638 cgi_set_content(&filebody);
639 }
640 if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found");
641 db_end_transaction(0);
642 return;
643
644 /* Jump here when unable to locate the document */
645 doc_not_found:
646 db_end_transaction(0);
@@ -528,10 +649,11 @@
649 @ <p>Document %h(zOrigName) not found
650 if( fossil_strcmp(zCheckin,"ckout")!=0 ){
651 @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
652 }
653 style_footer();
654 db_end_transaction(0);
655 return;
656 }
657
658 /*
659 ** The default logo.
@@ -653,5 +775,18 @@
775 }
776 cgi_set_content_type(zMime);
777 cgi_set_content(&bgimg);
778 g.isConst = 1;
779 }
780
781
782 /*
783 ** WEBPAGE: /docsrch
784 **
785 ** Search for documents that match a user-supplied pattern.
786 */
787 void doc_search_page(void){
788 login_check_credentials();
789 style_header("Document Search");
790 search_screen(SRCH_DOC, "docsrch");
791 style_footer();
792 }
793
+2 -2
--- src/glob.c
+++ src/glob.c
@@ -44,11 +44,11 @@
4444
const char *zSep = "(";
4545
int nTerm = 0;
4646
int i;
4747
int cTerm;
4848
49
- if( zGlobList==0 || zGlobList[0]==0 ) return "0";
49
+ if( zGlobList==0 || zGlobList[0]==0 ) return fossil_strdup("0");
5050
blob_zero(&expr);
5151
while( zGlobList[0] ){
5252
while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){
5353
zGlobList++; /* Skip leading commas, spaces, and newlines */
5454
}
@@ -73,11 +73,11 @@
7373
}
7474
if( nTerm ){
7575
blob_appendf(&expr, ")");
7676
return blob_str(&expr);
7777
}else{
78
- return "0";
78
+ return fossil_strdup("0");
7979
}
8080
}
8181
8282
#if INTERFACE
8383
/*
8484
--- src/glob.c
+++ src/glob.c
@@ -44,11 +44,11 @@
44 const char *zSep = "(";
45 int nTerm = 0;
46 int i;
47 int cTerm;
48
49 if( zGlobList==0 || zGlobList[0]==0 ) return "0";
50 blob_zero(&expr);
51 while( zGlobList[0] ){
52 while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){
53 zGlobList++; /* Skip leading commas, spaces, and newlines */
54 }
@@ -73,11 +73,11 @@
73 }
74 if( nTerm ){
75 blob_appendf(&expr, ")");
76 return blob_str(&expr);
77 }else{
78 return "0";
79 }
80 }
81
82 #if INTERFACE
83 /*
84
--- src/glob.c
+++ src/glob.c
@@ -44,11 +44,11 @@
44 const char *zSep = "(";
45 int nTerm = 0;
46 int i;
47 int cTerm;
48
49 if( zGlobList==0 || zGlobList[0]==0 ) return fossil_strdup("0");
50 blob_zero(&expr);
51 while( zGlobList[0] ){
52 while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){
53 zGlobList++; /* Skip leading commas, spaces, and newlines */
54 }
@@ -73,11 +73,11 @@
73 }
74 if( nTerm ){
75 blob_appendf(&expr, ")");
76 return blob_str(&expr);
77 }else{
78 return fossil_strdup("0");
79 }
80 }
81
82 #if INTERFACE
83 /*
84
--- src/json_config.c
+++ src/json_config.c
@@ -60,24 +60,29 @@
6060
{ "footer", CONFIGSET_SKIN },
6161
{ "logo-mimetype", CONFIGSET_SKIN },
6262
{ "logo-image", CONFIGSET_SKIN },
6363
{ "background-mimetype", CONFIGSET_SKIN },
6464
{ "background-image", CONFIGSET_SKIN },
65
-{ "index-page", CONFIGSET_SKIN },
6665
{ "timeline-block-markup", CONFIGSET_SKIN },
6766
{ "timeline-max-comment", CONFIGSET_SKIN },
6867
{ "timeline-plaintext", CONFIGSET_SKIN },
68
+{ "adunit", CONFIGSET_SKIN },
69
+{ "adunit-omit-if-admin", CONFIGSET_SKIN },
70
+{ "adunit-omit-if-user", CONFIGSET_SKIN },
71
+{ "white-foreground", CONFIGSET_SKIN },
6972
7073
{ "project-name", CONFIGSET_PROJ },
74
+{ "short-project-name", CONFIGSET_PROJ },
7175
{ "project-description", CONFIGSET_PROJ },
76
+{ "index-page", CONFIGSET_PROJ },
7277
{ "manifest", CONFIGSET_PROJ },
7378
{ "binary-glob", CONFIGSET_PROJ },
7479
{ "clean-glob", CONFIGSET_PROJ },
75
-{ "encoding-glob", CONFIGSET_PROJ },
7680
{ "ignore-glob", CONFIGSET_PROJ },
7781
{ "keep-glob", CONFIGSET_PROJ },
7882
{ "crnl-glob", CONFIGSET_PROJ },
83
+{ "encoding-glob", CONFIGSET_PROJ },
7984
{ "empty-dirs", CONFIGSET_PROJ },
8085
{ "allow-symlinks", CONFIGSET_PROJ },
8186
8287
{ "ticket-table", CONFIGSET_TKT },
8388
{ "ticket-common", CONFIGSET_TKT },
8489
--- src/json_config.c
+++ src/json_config.c
@@ -60,24 +60,29 @@
60 { "footer", CONFIGSET_SKIN },
61 { "logo-mimetype", CONFIGSET_SKIN },
62 { "logo-image", CONFIGSET_SKIN },
63 { "background-mimetype", CONFIGSET_SKIN },
64 { "background-image", CONFIGSET_SKIN },
65 { "index-page", CONFIGSET_SKIN },
66 { "timeline-block-markup", CONFIGSET_SKIN },
67 { "timeline-max-comment", CONFIGSET_SKIN },
68 { "timeline-plaintext", CONFIGSET_SKIN },
 
 
 
 
69
70 { "project-name", CONFIGSET_PROJ },
 
71 { "project-description", CONFIGSET_PROJ },
 
72 { "manifest", CONFIGSET_PROJ },
73 { "binary-glob", CONFIGSET_PROJ },
74 { "clean-glob", CONFIGSET_PROJ },
75 { "encoding-glob", CONFIGSET_PROJ },
76 { "ignore-glob", CONFIGSET_PROJ },
77 { "keep-glob", CONFIGSET_PROJ },
78 { "crnl-glob", CONFIGSET_PROJ },
 
79 { "empty-dirs", CONFIGSET_PROJ },
80 { "allow-symlinks", CONFIGSET_PROJ },
81
82 { "ticket-table", CONFIGSET_TKT },
83 { "ticket-common", CONFIGSET_TKT },
84
--- src/json_config.c
+++ src/json_config.c
@@ -60,24 +60,29 @@
60 { "footer", CONFIGSET_SKIN },
61 { "logo-mimetype", CONFIGSET_SKIN },
62 { "logo-image", CONFIGSET_SKIN },
63 { "background-mimetype", CONFIGSET_SKIN },
64 { "background-image", CONFIGSET_SKIN },
 
65 { "timeline-block-markup", CONFIGSET_SKIN },
66 { "timeline-max-comment", CONFIGSET_SKIN },
67 { "timeline-plaintext", CONFIGSET_SKIN },
68 { "adunit", CONFIGSET_SKIN },
69 { "adunit-omit-if-admin", CONFIGSET_SKIN },
70 { "adunit-omit-if-user", CONFIGSET_SKIN },
71 { "white-foreground", CONFIGSET_SKIN },
72
73 { "project-name", CONFIGSET_PROJ },
74 { "short-project-name", CONFIGSET_PROJ },
75 { "project-description", CONFIGSET_PROJ },
76 { "index-page", CONFIGSET_PROJ },
77 { "manifest", CONFIGSET_PROJ },
78 { "binary-glob", CONFIGSET_PROJ },
79 { "clean-glob", CONFIGSET_PROJ },
 
80 { "ignore-glob", CONFIGSET_PROJ },
81 { "keep-glob", CONFIGSET_PROJ },
82 { "crnl-glob", CONFIGSET_PROJ },
83 { "encoding-glob", CONFIGSET_PROJ },
84 { "empty-dirs", CONFIGSET_PROJ },
85 { "allow-symlinks", CONFIGSET_PROJ },
86
87 { "ticket-table", CONFIGSET_TKT },
88 { "ticket-common", CONFIGSET_TKT },
89
+2 -1
--- src/main.mk
+++ src/main.mk
@@ -449,11 +449,12 @@
449449
-DSQLITE_OMIT_LOAD_EXTENSION=1 \
450450
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
451451
-DSQLITE_THREADSAFE=0 \
452452
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
453453
-DSQLITE_OMIT_DEPRECATED \
454
- -DSQLITE_ENABLE_EXPLAIN_COMMENTS
454
+ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
455
+ -DSQLITE_ENABLE_FTS4
455456
456457
# Setup the options used to compile the included SQLite shell.
457458
SHELL_OPTIONS = -Dmain=sqlite3_shell \
458459
-DSQLITE_OMIT_LOAD_EXTENSION=1 \
459460
-DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
460461
--- src/main.mk
+++ src/main.mk
@@ -449,11 +449,12 @@
449 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
450 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
451 -DSQLITE_THREADSAFE=0 \
452 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
453 -DSQLITE_OMIT_DEPRECATED \
454 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
 
455
456 # Setup the options used to compile the included SQLite shell.
457 SHELL_OPTIONS = -Dmain=sqlite3_shell \
458 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
459 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
460
--- src/main.mk
+++ src/main.mk
@@ -449,11 +449,12 @@
449 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
450 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
451 -DSQLITE_THREADSAFE=0 \
452 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
453 -DSQLITE_OMIT_DEPRECATED \
454 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
455 -DSQLITE_ENABLE_FTS4
456
457 # Setup the options used to compile the included SQLite shell.
458 SHELL_OPTIONS = -Dmain=sqlite3_shell \
459 -DSQLITE_OMIT_LOAD_EXTENSION=1 \
460 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
461
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -157,10 +157,11 @@
157157
-DSQLITE_ENABLE_LOCKING_STYLE=0
158158
-DSQLITE_THREADSAFE=0
159159
-DSQLITE_DEFAULT_FILE_FORMAT=4
160160
-DSQLITE_OMIT_DEPRECATED
161161
-DSQLITE_ENABLE_EXPLAIN_COMMENTS
162
+ -DSQLITE_ENABLE_FTS4
162163
}
163164
#lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1
164165
#lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4
165166
#lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI
166167
#lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096
@@ -585,11 +586,11 @@
585586
#### The directories where the OpenSSL include and library files are located.
586587
# The recommended usage here is to use the Sysinternals junction tool
587588
# to create a hard link between an "openssl-1.x" sub-directory of the
588589
# Fossil source code directory and the target OpenSSL source directory.
589590
#
590
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
591
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
591592
OPENSSLINCDIR = $(OPENSSLDIR)/include
592593
OPENSSLLIBDIR = $(OPENSSLDIR)
593594
594595
#### Either the directory where the Tcl library is installed or the Tcl
595596
# source code directory resides (depending on the value of the macro
@@ -1318,11 +1319,11 @@
13181319
13191320
# Uncomment to enable Tcl support
13201321
# FOSSIL_ENABLE_TCL = 1
13211322
13221323
!ifdef FOSSIL_ENABLE_SSL
1323
-SSLDIR = $(B)\compat\openssl-1.0.1l
1324
+SSLDIR = $(B)\compat\openssl-1.0.2
13241325
SSLINCDIR = $(SSLDIR)\inc32
13251326
SSLLIBDIR = $(SSLDIR)\out32
13261327
SSLLFLAGS = /nologo /opt:ref /debug
13271328
SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
13281329
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
13291330
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -157,10 +157,11 @@
157 -DSQLITE_ENABLE_LOCKING_STYLE=0
158 -DSQLITE_THREADSAFE=0
159 -DSQLITE_DEFAULT_FILE_FORMAT=4
160 -DSQLITE_OMIT_DEPRECATED
161 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
 
162 }
163 #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1
164 #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4
165 #lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI
166 #lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096
@@ -585,11 +586,11 @@
585 #### The directories where the OpenSSL include and library files are located.
586 # The recommended usage here is to use the Sysinternals junction tool
587 # to create a hard link between an "openssl-1.x" sub-directory of the
588 # Fossil source code directory and the target OpenSSL source directory.
589 #
590 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
591 OPENSSLINCDIR = $(OPENSSLDIR)/include
592 OPENSSLLIBDIR = $(OPENSSLDIR)
593
594 #### Either the directory where the Tcl library is installed or the Tcl
595 # source code directory resides (depending on the value of the macro
@@ -1318,11 +1319,11 @@
1318
1319 # Uncomment to enable Tcl support
1320 # FOSSIL_ENABLE_TCL = 1
1321
1322 !ifdef FOSSIL_ENABLE_SSL
1323 SSLDIR = $(B)\compat\openssl-1.0.1l
1324 SSLINCDIR = $(SSLDIR)\inc32
1325 SSLLIBDIR = $(SSLDIR)\out32
1326 SSLLFLAGS = /nologo /opt:ref /debug
1327 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
1328 !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
1329
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -157,10 +157,11 @@
157 -DSQLITE_ENABLE_LOCKING_STYLE=0
158 -DSQLITE_THREADSAFE=0
159 -DSQLITE_DEFAULT_FILE_FORMAT=4
160 -DSQLITE_OMIT_DEPRECATED
161 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
162 -DSQLITE_ENABLE_FTS4
163 }
164 #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1
165 #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4
166 #lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI
167 #lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096
@@ -585,11 +586,11 @@
586 #### The directories where the OpenSSL include and library files are located.
587 # The recommended usage here is to use the Sysinternals junction tool
588 # to create a hard link between an "openssl-1.x" sub-directory of the
589 # Fossil source code directory and the target OpenSSL source directory.
590 #
591 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
592 OPENSSLINCDIR = $(OPENSSLDIR)/include
593 OPENSSLLIBDIR = $(OPENSSLDIR)
594
595 #### Either the directory where the Tcl library is installed or the Tcl
596 # source code directory resides (depending on the value of the macro
@@ -1318,11 +1319,11 @@
1319
1320 # Uncomment to enable Tcl support
1321 # FOSSIL_ENABLE_TCL = 1
1322
1323 !ifdef FOSSIL_ENABLE_SSL
1324 SSLDIR = $(B)\compat\openssl-1.0.2
1325 SSLINCDIR = $(SSLDIR)\inc32
1326 SSLLIBDIR = $(SSLDIR)\out32
1327 SSLLFLAGS = /nologo /opt:ref /debug
1328 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
1329 !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
1330
--- src/manifest.c
+++ src/manifest.c
@@ -600,10 +600,11 @@
600600
if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
601601
SYNTAX("incorrect J-card sort order");
602602
}
603603
break;
604604
}
605
+
605606
606607
/*
607608
** K <uuid>
608609
**
609610
** A K-line gives the UUID for the ticket which this control file
@@ -846,10 +847,11 @@
846847
if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
847848
x.z[0] = 0;
848849
x.z++;
849850
break;
850851
}
852
+
851853
852854
/*
853855
** Z <md5sum>
854856
**
855857
** MD5 checksum on this control file. The checksum is over all
@@ -1827,10 +1829,11 @@
18271829
for(i=0; i<p->nFile; i++){
18281830
add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
18291831
isPublic, 1, manifest_file_mperm(&p->aFile[i]));
18301832
}
18311833
}
1834
+ search_doc_touch('c', rid, 0);
18321835
db_multi_exec(
18331836
"REPLACE INTO event(type,mtime,objid,user,comment,"
18341837
"bgcolor,euser,ecomment,omtime)"
18351838
"VALUES('ci',"
18361839
" coalesce("
@@ -1932,10 +1935,11 @@
19321935
if( nWiki>0 ){
19331936
zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
19341937
}else{
19351938
zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
19361939
}
1940
+ search_doc_touch('w',rid,p->zWikiTitle);
19371941
db_multi_exec(
19381942
"REPLACE INTO event(type,mtime,objid,user,comment,"
19391943
" bgcolor,euser,ecomment)"
19401944
"VALUES('w',%.17g,%d,%Q,%Q,"
19411945
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1984,10 +1988,11 @@
19841988
}
19851989
}
19861990
if( subsequent ){
19871991
content_deltify(rid, subsequent, 0);
19881992
}else{
1993
+ search_doc_touch('e',rid,0);
19891994
db_multi_exec(
19901995
"REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
19911996
"VALUES('e',%.17g,%d,%d,%Q,%Q,"
19921997
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
19931998
p->rEventDate, rid, tagid, p->zUser, p->zComment,
19941999
--- src/manifest.c
+++ src/manifest.c
@@ -600,10 +600,11 @@
600 if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
601 SYNTAX("incorrect J-card sort order");
602 }
603 break;
604 }
 
605
606 /*
607 ** K <uuid>
608 **
609 ** A K-line gives the UUID for the ticket which this control file
@@ -846,10 +847,11 @@
846 if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
847 x.z[0] = 0;
848 x.z++;
849 break;
850 }
 
851
852 /*
853 ** Z <md5sum>
854 **
855 ** MD5 checksum on this control file. The checksum is over all
@@ -1827,10 +1829,11 @@
1827 for(i=0; i<p->nFile; i++){
1828 add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1829 isPublic, 1, manifest_file_mperm(&p->aFile[i]));
1830 }
1831 }
 
1832 db_multi_exec(
1833 "REPLACE INTO event(type,mtime,objid,user,comment,"
1834 "bgcolor,euser,ecomment,omtime)"
1835 "VALUES('ci',"
1836 " coalesce("
@@ -1932,10 +1935,11 @@
1932 if( nWiki>0 ){
1933 zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
1934 }else{
1935 zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
1936 }
 
1937 db_multi_exec(
1938 "REPLACE INTO event(type,mtime,objid,user,comment,"
1939 " bgcolor,euser,ecomment)"
1940 "VALUES('w',%.17g,%d,%Q,%Q,"
1941 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1984,10 +1988,11 @@
1984 }
1985 }
1986 if( subsequent ){
1987 content_deltify(rid, subsequent, 0);
1988 }else{
 
1989 db_multi_exec(
1990 "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
1991 "VALUES('e',%.17g,%d,%d,%Q,%Q,"
1992 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1993 p->rEventDate, rid, tagid, p->zUser, p->zComment,
1994
--- src/manifest.c
+++ src/manifest.c
@@ -600,10 +600,11 @@
600 if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){
601 SYNTAX("incorrect J-card sort order");
602 }
603 break;
604 }
605
606
607 /*
608 ** K <uuid>
609 **
610 ** A K-line gives the UUID for the ticket which this control file
@@ -846,10 +847,11 @@
847 if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated");
848 x.z[0] = 0;
849 x.z++;
850 break;
851 }
852
853
854 /*
855 ** Z <md5sum>
856 **
857 ** MD5 checksum on this control file. The checksum is over all
@@ -1827,10 +1829,11 @@
1829 for(i=0; i<p->nFile; i++){
1830 add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1831 isPublic, 1, manifest_file_mperm(&p->aFile[i]));
1832 }
1833 }
1834 search_doc_touch('c', rid, 0);
1835 db_multi_exec(
1836 "REPLACE INTO event(type,mtime,objid,user,comment,"
1837 "bgcolor,euser,ecomment,omtime)"
1838 "VALUES('ci',"
1839 " coalesce("
@@ -1932,10 +1935,11 @@
1935 if( nWiki>0 ){
1936 zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
1937 }else{
1938 zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
1939 }
1940 search_doc_touch('w',rid,p->zWikiTitle);
1941 db_multi_exec(
1942 "REPLACE INTO event(type,mtime,objid,user,comment,"
1943 " bgcolor,euser,ecomment)"
1944 "VALUES('w',%.17g,%d,%Q,%Q,"
1945 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -1984,10 +1988,11 @@
1988 }
1989 }
1990 if( subsequent ){
1991 content_deltify(rid, subsequent, 0);
1992 }else{
1993 search_doc_touch('e',rid,0);
1994 db_multi_exec(
1995 "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
1996 "VALUES('e',%.17g,%d,%d,%Q,%Q,"
1997 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
1998 p->rEventDate, rid, tagid, p->zUser, p->zComment,
1999
+11 -3
--- src/rebuild.c
+++ src/rebuild.c
@@ -533,10 +533,12 @@
533533
** --vacuum Run VACUUM on the database after rebuilding
534534
** --deanalyze Remove ANALYZE tables from the database
535535
** --analyze Run ANALYZE on the database after rebuilding
536536
** --wal Set Write-Ahead-Log journalling mode on the database
537537
** --stats Show artifact statistics after rebuilding
538
+** --index Always add in the full-text search index
539
+** --no-index Always omit the full-text search index
538540
**
539541
** See also: deconstruct, reconstruct
540542
*/
541543
void rebuild_database(void){
542544
int forceFlag;
@@ -550,10 +552,11 @@
550552
int runVacuum;
551553
int runDeanalyze;
552554
int runAnalyze;
553555
int runCompress;
554556
int showStats;
557
+ int runReindex;
555558
556559
omitVerify = find_option("noverify",0,0)!=0;
557560
forceFlag = find_option("force","f",0)!=0;
558561
randomizeFlag = find_option("randomize", 0, 0)!=0;
559562
doClustering = find_option("cluster", 0, 0)!=0;
@@ -580,15 +583,19 @@
580583
usage("?REPOSITORY-FILENAME?");
581584
}
582585
db_close(1);
583586
db_open_repository(g.zRepositoryName);
584587
}
585
-
588
+ runReindex = search_index_exists();
589
+ if( find_option("index",0,0)!=0 ) runReindex = 1;
590
+ if( find_option("no-index",0,0)!=0 ) runReindex = 0;
591
+
586592
/* We should be done with options.. */
587593
verify_all_options();
588594
589595
db_begin_transaction();
596
+ search_drop_index();
590597
ttyOutput = 1;
591598
errCnt = rebuild_db(randomizeFlag, 1, doClustering);
592599
reconstruct_private_table();
593600
db_multi_exec(
594601
"REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());"
@@ -634,10 +641,11 @@
634641
}
635642
if( activateWal ){
636643
db_multi_exec("PRAGMA journal_mode=WAL;");
637644
}
638645
}
646
+ if( runReindex ) search_rebuild_index();
639647
if( showStats ){
640648
static const struct { int idx; const char *zLabel; } aStat[] = {
641649
{ CFTYPE_ANY, "Artifacts:" },
642650
{ CFTYPE_MANIFEST, "Manifests:" },
643651
{ CFTYPE_CLUSTER, "Clusters:" },
@@ -799,11 +807,11 @@
799807
int privateOnly = find_option("private",0,0)!=0;
800808
int bNeedRebuild = 0;
801809
db_find_and_open_repository(OPEN_ANY_SCHEMA, 2);
802810
db_close(1);
803811
db_open_repository(g.zRepositoryName);
804
-
812
+
805813
/* We should be done with options.. */
806814
verify_all_options();
807815
808816
if( !bForce ){
809817
Blob ans;
@@ -929,11 +937,11 @@
929937
fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
930938
usage("FILENAME DIRECTORY");
931939
}
932940
db_create_repository(g.argv[2]);
933941
db_open_repository(g.argv[2]);
934
-
942
+
935943
/* We should be done with options.. */
936944
verify_all_options();
937945
938946
db_open_config(0);
939947
db_begin_transaction();
940948
--- src/rebuild.c
+++ src/rebuild.c
@@ -533,10 +533,12 @@
533 ** --vacuum Run VACUUM on the database after rebuilding
534 ** --deanalyze Remove ANALYZE tables from the database
535 ** --analyze Run ANALYZE on the database after rebuilding
536 ** --wal Set Write-Ahead-Log journalling mode on the database
537 ** --stats Show artifact statistics after rebuilding
 
 
538 **
539 ** See also: deconstruct, reconstruct
540 */
541 void rebuild_database(void){
542 int forceFlag;
@@ -550,10 +552,11 @@
550 int runVacuum;
551 int runDeanalyze;
552 int runAnalyze;
553 int runCompress;
554 int showStats;
 
555
556 omitVerify = find_option("noverify",0,0)!=0;
557 forceFlag = find_option("force","f",0)!=0;
558 randomizeFlag = find_option("randomize", 0, 0)!=0;
559 doClustering = find_option("cluster", 0, 0)!=0;
@@ -580,15 +583,19 @@
580 usage("?REPOSITORY-FILENAME?");
581 }
582 db_close(1);
583 db_open_repository(g.zRepositoryName);
584 }
585
 
 
 
586 /* We should be done with options.. */
587 verify_all_options();
588
589 db_begin_transaction();
 
590 ttyOutput = 1;
591 errCnt = rebuild_db(randomizeFlag, 1, doClustering);
592 reconstruct_private_table();
593 db_multi_exec(
594 "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());"
@@ -634,10 +641,11 @@
634 }
635 if( activateWal ){
636 db_multi_exec("PRAGMA journal_mode=WAL;");
637 }
638 }
 
639 if( showStats ){
640 static const struct { int idx; const char *zLabel; } aStat[] = {
641 { CFTYPE_ANY, "Artifacts:" },
642 { CFTYPE_MANIFEST, "Manifests:" },
643 { CFTYPE_CLUSTER, "Clusters:" },
@@ -799,11 +807,11 @@
799 int privateOnly = find_option("private",0,0)!=0;
800 int bNeedRebuild = 0;
801 db_find_and_open_repository(OPEN_ANY_SCHEMA, 2);
802 db_close(1);
803 db_open_repository(g.zRepositoryName);
804
805 /* We should be done with options.. */
806 verify_all_options();
807
808 if( !bForce ){
809 Blob ans;
@@ -929,11 +937,11 @@
929 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
930 usage("FILENAME DIRECTORY");
931 }
932 db_create_repository(g.argv[2]);
933 db_open_repository(g.argv[2]);
934
935 /* We should be done with options.. */
936 verify_all_options();
937
938 db_open_config(0);
939 db_begin_transaction();
940
--- src/rebuild.c
+++ src/rebuild.c
@@ -533,10 +533,12 @@
533 ** --vacuum Run VACUUM on the database after rebuilding
534 ** --deanalyze Remove ANALYZE tables from the database
535 ** --analyze Run ANALYZE on the database after rebuilding
536 ** --wal Set Write-Ahead-Log journalling mode on the database
537 ** --stats Show artifact statistics after rebuilding
538 ** --index Always add in the full-text search index
539 ** --no-index Always omit the full-text search index
540 **
541 ** See also: deconstruct, reconstruct
542 */
543 void rebuild_database(void){
544 int forceFlag;
@@ -550,10 +552,11 @@
552 int runVacuum;
553 int runDeanalyze;
554 int runAnalyze;
555 int runCompress;
556 int showStats;
557 int runReindex;
558
559 omitVerify = find_option("noverify",0,0)!=0;
560 forceFlag = find_option("force","f",0)!=0;
561 randomizeFlag = find_option("randomize", 0, 0)!=0;
562 doClustering = find_option("cluster", 0, 0)!=0;
@@ -580,15 +583,19 @@
583 usage("?REPOSITORY-FILENAME?");
584 }
585 db_close(1);
586 db_open_repository(g.zRepositoryName);
587 }
588 runReindex = search_index_exists();
589 if( find_option("index",0,0)!=0 ) runReindex = 1;
590 if( find_option("no-index",0,0)!=0 ) runReindex = 0;
591
592 /* We should be done with options.. */
593 verify_all_options();
594
595 db_begin_transaction();
596 search_drop_index();
597 ttyOutput = 1;
598 errCnt = rebuild_db(randomizeFlag, 1, doClustering);
599 reconstruct_private_table();
600 db_multi_exec(
601 "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());"
@@ -634,10 +641,11 @@
641 }
642 if( activateWal ){
643 db_multi_exec("PRAGMA journal_mode=WAL;");
644 }
645 }
646 if( runReindex ) search_rebuild_index();
647 if( showStats ){
648 static const struct { int idx; const char *zLabel; } aStat[] = {
649 { CFTYPE_ANY, "Artifacts:" },
650 { CFTYPE_MANIFEST, "Manifests:" },
651 { CFTYPE_CLUSTER, "Clusters:" },
@@ -799,11 +807,11 @@
807 int privateOnly = find_option("private",0,0)!=0;
808 int bNeedRebuild = 0;
809 db_find_and_open_repository(OPEN_ANY_SCHEMA, 2);
810 db_close(1);
811 db_open_repository(g.zRepositoryName);
812
813 /* We should be done with options.. */
814 verify_all_options();
815
816 if( !bForce ){
817 Blob ans;
@@ -929,11 +937,11 @@
937 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
938 usage("FILENAME DIRECTORY");
939 }
940 db_create_repository(g.argv[2]);
941 db_open_repository(g.argv[2]);
942
943 /* We should be done with options.. */
944 verify_all_options();
945
946 db_open_config(0);
947 db_begin_transaction();
948
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
2929
# define SQLITE_RECURSIVE 33
3030
#endif
3131
3232
/*
3333
** WEBPAGE: /reportlist
34
+**
35
+** Main menu for Tickets.
3436
*/
3537
void view_list(void){
3638
const char *zScript;
3739
Blob ril; /* Report Item List */
3840
Stmt q;
@@ -40,10 +42,11 @@
4042
int cnt = 0;
4143
4244
login_check_credentials();
4345
if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
4446
style_header("Ticket Main Menu");
47
+ ticket_standard_submenu(T_ALL_BUT(T_REPLIST));
4548
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
4649
zScript = ticket_reportlist_code();
4750
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
4851
4952
blob_zero(&ril);
5053
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /*
33 ** WEBPAGE: /reportlist
 
 
34 */
35 void view_list(void){
36 const char *zScript;
37 Blob ril; /* Report Item List */
38 Stmt q;
@@ -40,10 +42,11 @@
40 int cnt = 0;
41
42 login_check_credentials();
43 if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
44 style_header("Ticket Main Menu");
 
45 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
46 zScript = ticket_reportlist_code();
47 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
48
49 blob_zero(&ril);
50
--- src/report.c
+++ src/report.c
@@ -29,10 +29,12 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /*
33 ** WEBPAGE: /reportlist
34 **
35 ** Main menu for Tickets.
36 */
37 void view_list(void){
38 const char *zScript;
39 Blob ril; /* Report Item List */
40 Stmt q;
@@ -40,10 +42,11 @@
42 int cnt = 0;
43
44 login_check_credentials();
45 if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; }
46 style_header("Ticket Main Menu");
47 ticket_standard_submenu(T_ALL_BUT(T_REPLIST));
48 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1);
49 zScript = ticket_reportlist_code();
50 if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1);
51
52 blob_zero(&ril);
53
+2 -2
--- src/rss.c
+++ src/rss.c
@@ -65,11 +65,11 @@
6565
@ FROM event, blob
6666
@ WHERE blob.rid=event.objid
6767
;
6868
6969
login_check_credentials();
70
- if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
70
+ if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
7171
return;
7272
}
7373
7474
blob_zero(&bSQL);
7575
blob_append( &bSQL, zSQL1, -1 );
@@ -83,11 +83,11 @@
8383
if( !g.perm.Read ){
8484
if( g.perm.RdTkt && g.perm.RdWiki ){
8585
blob_append(&bSQL, " AND event.type!='ci'", -1);
8686
}else if( g.perm.RdTkt ){
8787
blob_append(&bSQL, " AND event.type=='t'", -1);
88
-
88
+
8989
}else{
9090
blob_append(&bSQL, " AND event.type=='w'", -1);
9191
}
9292
}else if( !g.perm.RdWiki ){
9393
if( g.perm.RdTkt ){
9494
--- src/rss.c
+++ src/rss.c
@@ -65,11 +65,11 @@
65 @ FROM event, blob
66 @ WHERE blob.rid=event.objid
67 ;
68
69 login_check_credentials();
70 if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
71 return;
72 }
73
74 blob_zero(&bSQL);
75 blob_append( &bSQL, zSQL1, -1 );
@@ -83,11 +83,11 @@
83 if( !g.perm.Read ){
84 if( g.perm.RdTkt && g.perm.RdWiki ){
85 blob_append(&bSQL, " AND event.type!='ci'", -1);
86 }else if( g.perm.RdTkt ){
87 blob_append(&bSQL, " AND event.type=='t'", -1);
88
89 }else{
90 blob_append(&bSQL, " AND event.type=='w'", -1);
91 }
92 }else if( !g.perm.RdWiki ){
93 if( g.perm.RdTkt ){
94
--- src/rss.c
+++ src/rss.c
@@ -65,11 +65,11 @@
65 @ FROM event, blob
66 @ WHERE blob.rid=event.objid
67 ;
68
69 login_check_credentials();
70 if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
71 return;
72 }
73
74 blob_zero(&bSQL);
75 blob_append( &bSQL, zSQL1, -1 );
@@ -83,11 +83,11 @@
83 if( !g.perm.Read ){
84 if( g.perm.RdTkt && g.perm.RdWiki ){
85 blob_append(&bSQL, " AND event.type!='ci'", -1);
86 }else if( g.perm.RdTkt ){
87 blob_append(&bSQL, " AND event.type=='t'", -1);
88
89 }else{
90 blob_append(&bSQL, " AND event.type=='w'", -1);
91 }
92 }else if( !g.perm.RdWiki ){
93 if( g.perm.RdTkt ){
94
+1 -1
--- src/schema.c
+++ src/schema.c
@@ -230,11 +230,11 @@
230230
@ name TEXT UNIQUE -- Name of file page
231231
@ );
232232
@
233233
@ -- Linkages between checkins, files created by each checkin, and
234234
@ -- the names of those files.
235
-@ --
235
+@ --
236236
@ -- Each entry represents a file that changed content from pid to fid
237237
@ -- due to the check-in that goes from pmid to mid. fnid is the name
238238
@ -- of the file in the mid check-in. If the file was renamed as part
239239
@ -- of the mid check-in, then pfnid is the previous filename.
240240
@
241241
--- src/schema.c
+++ src/schema.c
@@ -230,11 +230,11 @@
230 @ name TEXT UNIQUE -- Name of file page
231 @ );
232 @
233 @ -- Linkages between checkins, files created by each checkin, and
234 @ -- the names of those files.
235 @ --
236 @ -- Each entry represents a file that changed content from pid to fid
237 @ -- due to the check-in that goes from pmid to mid. fnid is the name
238 @ -- of the file in the mid check-in. If the file was renamed as part
239 @ -- of the mid check-in, then pfnid is the previous filename.
240 @
241
--- src/schema.c
+++ src/schema.c
@@ -230,11 +230,11 @@
230 @ name TEXT UNIQUE -- Name of file page
231 @ );
232 @
233 @ -- Linkages between checkins, files created by each checkin, and
234 @ -- the names of those files.
235 @ --
236 @ -- Each entry represents a file that changed content from pid to fid
237 @ -- due to the check-in that goes from pmid to mid. fnid is the name
238 @ -- of the file in the mid check-in. If the file was renamed as part
239 @ -- of the mid check-in, then pfnid is the previous filename.
240 @
241
+1130 -65
--- src/search.c
+++ src/search.c
@@ -40,20 +40,29 @@
4040
struct srchTerm { /* For each search term */
4141
char *z; /* Text */
4242
int n; /* length */
4343
} a[SEARCH_MAX_TERM];
4444
/* Snippet controls */
45
- char *zMarkBegin; /* Start of a match */
45
+ char *zPattern; /* The search pattern */
46
+ char *zMarkBegin; /* Start of a match */
4647
char *zMarkEnd; /* End of a match */
4748
char *zMarkGap; /* A gap between two matches */
4849
unsigned fSrchFlg; /* Flags */
50
+ int iScore; /* Score of the last match attempt */
51
+ Blob snip; /* Snippet for the most recent match */
4952
};
5053
51
-#define SRCHFLG_HTML 0x0001 /* Escape snippet text for HTML */
54
+#define SRCHFLG_HTML 0x01 /* Escape snippet text for HTML */
55
+#define SRCHFLG_STATIC 0x04 /* The static gSearch object */
5256
5357
#endif
5458
59
+/*
60
+** There is a single global Search object:
61
+*/
62
+static Search gSearch;
63
+
5564
5665
/*
5766
** Theses characters constitute a word boundary
5867
*/
5968
static const char isBoundary[] = {
@@ -74,23 +83,53 @@
7483
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7584
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7685
};
7786
#define ISALNUM(x) (!isBoundary[(x)&0xff])
7887
88
+
89
+/*
90
+** Destroy a search context.
91
+*/
92
+void search_end(Search *p){
93
+ if( p ){
94
+ fossil_free(p->zPattern);
95
+ fossil_free(p->zMarkBegin);
96
+ fossil_free(p->zMarkEnd);
97
+ fossil_free(p->zMarkGap);
98
+ if( p->iScore ) blob_reset(&p->snip);
99
+ memset(p, 0, sizeof(*p));
100
+ if( p!=&gSearch ) fossil_free(p);
101
+ }
102
+}
103
+
79104
/*
80105
** Compile a search pattern
81106
*/
82
-Search *search_init(const char *zPattern){
83
- int nPattern = strlen(zPattern);
107
+Search *search_init(
108
+ const char *zPattern, /* The search pattern */
109
+ const char *zMarkBegin, /* Start of a match */
110
+ const char *zMarkEnd, /* End of a match */
111
+ const char *zMarkGap, /* A gap between two matches */
112
+ unsigned fSrchFlg /* Flags */
113
+){
84114
Search *p;
85115
char *z;
86116
int i;
87117
88
- p = fossil_malloc( nPattern + sizeof(*p) + 1);
89
- z = (char*)&p[1];
90
- memcpy(z, zPattern, nPattern+1);
91
- memset(p, 0, sizeof(*p));
118
+ if( fSrchFlg & SRCHFLG_STATIC ){
119
+ p = &gSearch;
120
+ search_end(p);
121
+ }else{
122
+ p = fossil_malloc(sizeof(*p));
123
+ memset(p, 0, sizeof(*p));
124
+ }
125
+ p->zPattern = z = mprintf("%s", zPattern);
126
+ p->zMarkBegin = mprintf("%s", zMarkBegin);
127
+ p->zMarkEnd = mprintf("%s", zMarkEnd);
128
+ p->zMarkGap = mprintf("%s", zMarkGap);
129
+ p->fSrchFlg = fSrchFlg;
130
+ blob_init(&p->snip, 0, 0);
92131
while( *z && p->nTerm<SEARCH_MAX_TERM ){
93132
while( *z && !ISALNUM(*z) ){ z++; }
94133
if( *z==0 ) break;
95134
p->a[p->nTerm].z = z;
96135
for(i=1; ISALNUM(z[i]); i++){}
@@ -100,49 +139,46 @@
100139
}
101140
return p;
102141
}
103142
104143
105
-/*
106
-** Destroy a search context.
107
-*/
108
-void search_end(Search *p){
109
- free(p);
110
-}
111
-
112144
/*
113145
** Append n bytes of text to snippet zTxt. Encode the text appropriately.
114146
*/
115147
static void snippet_text_append(
116148
Search *p, /* The search context */
117149
Blob *pSnip, /* Append to this snippet */
118150
const char *zTxt, /* Text to append */
119151
int n /* How many bytes to append */
120152
){
121
- if( p->fSrchFlg & SRCHFLG_HTML ){
122
- blob_appendf(pSnip, "%.*h", n, zTxt);
123
- }else{
124
- blob_append(pSnip, zTxt, n);
153
+ if( n>0 ){
154
+ if( p->fSrchFlg & SRCHFLG_HTML ){
155
+ blob_appendf(pSnip, "%#h", n, zTxt);
156
+ }else{
157
+ blob_append(pSnip, zTxt, n);
158
+ }
125159
}
126160
}
127161
128162
/*
129163
** Compare a search pattern against one or more input strings which
130
-** collectively comprise a document. Return a match score. Optionally
131
-** also return a "snippet".
164
+** collectively comprise a document. Return a match score. Any
165
+** postive value means there was a match. Zero means that one or
166
+** more terms are missing.
167
+**
168
+** The score and a snippet are record for future use.
132169
**
133170
** Scoring:
134171
** * All terms must match at least once or the score is zero
135172
** * One point for each matching term
136173
** * Extra points if consecutive words of the pattern are consecutive
137174
** in the document
138175
*/
139
-static int search_score(
176
+static int search_match(
140177
Search *p, /* Search pattern and flags */
141178
int nDoc, /* Number of strings in this document */
142
- const char **azDoc, /* Text of each string */
143
- Blob *pSnip /* If not NULL: Write a snippet here */
179
+ const char **azDoc /* Text of each string */
144180
){
145181
int score; /* Final score */
146182
int i; /* Offset into current document */
147183
int ii; /* Loop counter */
148184
int j; /* Loop over search terms */
@@ -186,25 +222,26 @@
186222
}
187223
break;
188224
}
189225
}
190226
while( ISALNUM(zDoc[i]) ){ i++; }
227
+ if( zDoc[i]==0 ) break;
191228
}
192229
}
193230
194231
/* Finished search all documents.
195
- ** Every term must be seen or else the score is zero
232
+ ** Every term must be seen or else the score is zero
196233
*/
197234
score = 1;
198235
for(j=0; j<p->nTerm; j++) score *= anMatch[j];
199
- if( score==0 || pSnip==0 ) return score;
236
+ blob_reset(&p->snip);
237
+ p->iScore = score;
238
+ if( score==0 ) return score;
200239
201240
202241
/* Prepare a snippet that describes the matching text.
203242
*/
204
- blob_init(pSnip, 0, 0);
205
-
206243
while(1){
207244
int iOfst;
208245
int iTail;
209246
int iBest;
210247
for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){}
@@ -239,11 +276,11 @@
239276
if( iOfst<0 ) iOfst = 0;
240277
while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--;
241278
while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++;
242279
for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){}
243280
while( ISALNUM(zDoc[iTail]) ) iTail++;
244
- if( iOfst>0 || wantGap ) blob_append(pSnip, p->zMarkGap, -1);
281
+ if( iOfst>0 || wantGap ) blob_append(&p->snip, p->zMarkGap, -1);
245282
wantGap = zDoc[iTail]!=0;
246283
zDoc += iOfst;
247284
iTail -= iOfst;
248285
249286
/* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */
@@ -252,94 +289,196 @@
252289
for(j=0; j<p->nTerm; j++){
253290
int n = p->a[j].n;
254291
if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0
255292
&& (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*')
256293
){
257
- snippet_text_append(p, pSnip, zDoc, i);
294
+ snippet_text_append(p, &p->snip, zDoc, i);
258295
zDoc += i;
259296
iTail -= i;
260
- blob_append(pSnip, p->zMarkBegin, -1);
297
+ blob_append(&p->snip, p->zMarkBegin, -1);
261298
if( p->a[j].z[n]=='*' ){
262299
while( ISALNUM(zDoc[n]) ) n++;
263300
}
264
- snippet_text_append(p, pSnip, zDoc, n);
301
+ snippet_text_append(p, &p->snip, zDoc, n);
265302
zDoc += n;
266303
iTail -= n;
267
- blob_append(pSnip, p->zMarkEnd, -1);
304
+ blob_append(&p->snip, p->zMarkEnd, -1);
268305
i = -1;
269306
break;
270307
} /* end-if */
271308
} /* end for(j) */
272309
if( j<p->nTerm ){
273310
while( ISALNUM(zDoc[i]) && i<iTail ){ i++; }
274311
}
275312
} /* end for(i) */
276
- if( iTail>0 ) snippet_text_append(p, pSnip, zDoc, iTail);
313
+ snippet_text_append(p, &p->snip, zDoc, iTail);
277314
}
278
- if( wantGap ) blob_append(pSnip, p->zMarkGap, -1);
315
+ if( wantGap ) blob_append(&p->snip, p->zMarkGap, -1);
279316
return score;
280317
}
281318
282319
/*
283
-** COMMAND: test-snippet
320
+** COMMAND: test-match
284321
**
285
-** Usage: fossil test-snippet SEARCHSTRING FILE1 FILE2 ...
322
+** Usage: fossil test-match SEARCHSTRING FILE1 FILE2 ...
286323
*/
287
-void test_snippet_cmd(void){
324
+void test_match_cmd(void){
288325
Search *p;
289326
int i;
290327
Blob x;
291
- Blob snip;
292328
int score;
293329
char *zDoc;
330
+ int flg = 0;
331
+ char *zBegin = (char*)find_option("begin",0,1);
332
+ char *zEnd = (char*)find_option("end",0,1);
333
+ char *zGap = (char*)find_option("gap",0,1);
334
+ if( find_option("html",0,0)!=0 ) flg |= SRCHFLG_HTML;
335
+ if( find_option("static",0,0)!=0 ) flg |= SRCHFLG_STATIC;
336
+ verify_all_options();
294337
if( g.argc<4 ) usage("SEARCHSTRING FILE1...");
295
- p = search_init(g.argv[2]);
296
- p->zMarkBegin = "[[";
297
- p->zMarkEnd = "]]";
298
- p->zMarkGap = " ... ";
338
+ if( zBegin==0 ) zBegin = "[[";
339
+ if( zEnd==0 ) zEnd = "]]";
340
+ if( zGap==0 ) zGap = " ... ";
341
+ p = search_init(g.argv[2], zBegin, zEnd, zGap, flg);
299342
for(i=3; i<g.argc; i++){
300343
blob_read_from_file(&x, g.argv[i]);
301344
zDoc = blob_str(&x);
302
- score = search_score(p, 1, (const char**)&zDoc, &snip);
303
- fossil_print("%s: %d\n", g.argv[i], score);
345
+ score = search_match(p, 1, (const char**)&zDoc);
346
+ fossil_print("%s: %d\n", g.argv[i], p->iScore);
304347
blob_reset(&x);
305348
if( score ){
306
- fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&snip), '=');
307
- blob_reset(&snip);
349
+ fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&p->snip), '=');
308350
}
309
- }
351
+ }
352
+ search_end(p);
353
+}
354
+
355
+/*
356
+** An SQL function to initialize the global search pattern:
357
+**
358
+** search_init(PATTERN,BEGIN,END,GAP,FLAGS)
359
+**
360
+** All arguments are optional.
361
+*/
362
+static void search_init_sqlfunc(
363
+ sqlite3_context *context,
364
+ int argc,
365
+ sqlite3_value **argv
366
+){
367
+ const char *zPattern = 0;
368
+ const char *zBegin = "<mark>";
369
+ const char *zEnd = "</mark>";
370
+ const char *zGap = " ... ";
371
+ unsigned int flg = SRCHFLG_HTML;
372
+ switch( argc ){
373
+ default:
374
+ flg = (unsigned int)sqlite3_value_int(argv[4]);
375
+ case 4:
376
+ zGap = (const char*)sqlite3_value_text(argv[3]);
377
+ case 3:
378
+ zEnd = (const char*)sqlite3_value_text(argv[2]);
379
+ case 2:
380
+ zBegin = (const char*)sqlite3_value_text(argv[1]);
381
+ case 1:
382
+ zPattern = (const char*)sqlite3_value_text(argv[0]);
383
+ }
384
+ if( zPattern && zPattern[0] ){
385
+ search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC);
386
+ }else{
387
+ search_end(&gSearch);
388
+ }
389
+}
390
+
391
+/*
392
+** Try to match the input text against the search parameters set up
393
+** by the previous search_init() call. Remember the results globally.
394
+** Return non-zero on a match and zero on a miss.
395
+*/
396
+static void search_match_sqlfunc(
397
+ sqlite3_context *context,
398
+ int argc,
399
+ sqlite3_value **argv
400
+){
401
+ const char *zSText = (const char*)sqlite3_value_text(argv[0]);
402
+ int rc;
403
+ if( zSText==0 ) return;
404
+ rc = search_match(&gSearch, 1, &zSText);
405
+ sqlite3_result_int(context, rc);
310406
}
311407
312408
/*
313
-** This is an SQLite function that scores its input using
314
-** a pre-computed pattern.
409
+** These SQL functions return the results of the last
410
+** call to the search_match() SQL function.
315411
*/
316412
static void search_score_sqlfunc(
317413
sqlite3_context *context,
318414
int argc,
319415
sqlite3_value **argv
320416
){
321
- Search *p = (Search*)sqlite3_user_data(context);
322
- const char **azDoc;
323
- int score;
324
- int i;
325
-
326
- azDoc = fossil_malloc( sizeof(const char*)*(argc+1) );
327
- for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]);
328
- score = search_score(p, argc, azDoc, 0);
329
- fossil_free((void *)azDoc);
330
- sqlite3_result_int(context, score);
417
+ sqlite3_result_int(context, gSearch.iScore);
418
+}
419
+static void search_snippet_sqlfunc(
420
+ sqlite3_context *context,
421
+ int argc,
422
+ sqlite3_value **argv
423
+){
424
+ if( blob_size(&gSearch.snip)>0 ){
425
+ sqlite3_result_text(context, blob_str(&gSearch.snip), -1, fossil_free);
426
+ blob_init(&gSearch.snip, 0, 0);
427
+ }
428
+}
429
+
430
+/*
431
+** This is an SQLite function that computes the searchable text.
432
+** It is a wrapper around the search_stext() routine. See the
433
+** search_stext() routine for further detail.
434
+*/
435
+static void search_stext_sqlfunc(
436
+ sqlite3_context *context,
437
+ int argc,
438
+ sqlite3_value **argv
439
+){
440
+ Blob txt;
441
+ const char *zType = (const char*)sqlite3_value_text(argv[0]);
442
+ int rid = sqlite3_value_int(argv[1]);
443
+ const char *zName = (const char*)sqlite3_value_text(argv[2]);
444
+ search_stext(zType[0], rid, zName, &txt);
445
+ sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free);
446
+}
447
+
448
+/*
449
+** Encode a string for use as a query parameter in a URL
450
+*/
451
+static void search_urlencode_sqlfunc(
452
+ sqlite3_context *context,
453
+ int argc,
454
+ sqlite3_value **argv
455
+){
456
+ char *z = mprintf("%T",sqlite3_value_text(argv[0]));
457
+ sqlite3_result_text(context, z, -1, fossil_free);
331458
}
332459
333460
/*
334461
** Register the "score()" SQL function to score its input text
335462
** using the given Search object. Once this function is registered,
336463
** do not delete the Search object.
337464
*/
338
-void search_sql_setup(Search *p){
339
- sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p,
465
+void search_sql_setup(sqlite3 *db){
466
+ static int once = 0;
467
+ if( once++ ) return;
468
+ sqlite3_create_function(db, "search_match", 1, SQLITE_UTF8, 0,
469
+ search_match_sqlfunc, 0, 0);
470
+ sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0,
340471
search_score_sqlfunc, 0, 0);
472
+ sqlite3_create_function(db, "search_snippet", 0, SQLITE_UTF8, 0,
473
+ search_snippet_sqlfunc, 0, 0);
474
+ sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
475
+ search_init_sqlfunc, 0, 0);
476
+ sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0,
477
+ search_stext_sqlfunc, 0, 0);
478
+ sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
479
+ search_urlencode_sqlfunc, 0, 0);
341480
}
342481
343482
/*
344483
** Testing the search function.
345484
**
@@ -357,11 +496,10 @@
357496
** of entries returned. The -width option can be
358497
** used to set the output width used when printing
359498
** matches.
360499
*/
361500
void search_cmd(void){
362
- Search *p;
363501
Blob pattern;
364502
int i;
365503
Blob sql = empty_blob;
366504
Stmt q;
367505
int iBest;
@@ -386,13 +524,13 @@
386524
if( g.argc<2 ) return;
387525
blob_init(&pattern, g.argv[2], -1);
388526
for(i=3; i<g.argc; i++){
389527
blob_appendf(&pattern, " %s", g.argv[i]);
390528
}
391
- p = search_init(blob_str(&pattern));
529
+ (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
392530
blob_reset(&pattern);
393
- search_sql_setup(p);
531
+ search_sql_setup(g.db);
394532
395533
db_multi_exec(
396534
"CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
397535
"CREATE INDEX srch_idx1 ON srch(x);"
398536
"INSERT INTO srch(rid,uuid,date,comment,x)"
@@ -414,5 +552,932 @@
414552
db_prepare(&q, "%s", blob_sql_text(&sql));
415553
blob_reset(&sql);
416554
print_timeline(&q, nLimit, width, 0);
417555
db_finalize(&q);
418556
}
557
+
558
+#if INTERFACE
559
+/* What to search for */
560
+#define SRCH_CKIN 0x0001 /* Search over check-in comments */
561
+#define SRCH_DOC 0x0002 /* Search over embedded documents */
562
+#define SRCH_TKT 0x0004 /* Search over tickets */
563
+#define SRCH_WIKI 0x0008 /* Search over wiki */
564
+#define SRCH_ALL 0x000f /* Search over everything */
565
+#endif
566
+
567
+/*
568
+** Remove bits from srchFlags which are disallowed by either the
569
+** current server configuration or by user permissions.
570
+*/
571
+unsigned int search_restrict(unsigned int srchFlags){
572
+ if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC);
573
+ if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT);
574
+ if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
575
+ if( (srchFlags & SRCH_CKIN)!=0 && db_get_boolean("search-ci",0)==0 ){
576
+ srchFlags &= ~SRCH_CKIN;
577
+ }
578
+ if( (srchFlags & SRCH_DOC)!=0 && db_get_boolean("search-doc",0)==0 ){
579
+ srchFlags &= ~SRCH_DOC;
580
+ }
581
+ if( (srchFlags & SRCH_TKT)!=0 && db_get_boolean("search-tkt",0)==0 ){
582
+ srchFlags &= ~SRCH_TKT;
583
+ }
584
+ if( (srchFlags & SRCH_WIKI)!=0 && db_get_boolean("search-wiki",0)==0 ){
585
+ srchFlags &= ~SRCH_WIKI;
586
+ }
587
+ return srchFlags;
588
+}
589
+
590
+/*
591
+** When this routine is called, there already exists a table
592
+**
593
+** x(label,url,score,date,snip).
594
+**
595
+** And the srchFlags parameter has been validated. This routine
596
+** fills the X table with search results using a full-text scan.
597
+**
598
+** The companion indexed scan routine is search_indexed().
599
+*/
600
+static void search_fullscan(
601
+ const char *zPattern, /* The query pattern */
602
+ unsigned int srchFlags /* What to search over */
603
+){
604
+ search_init(zPattern, "<mark>", "</mark>", " ... ",
605
+ SRCHFLG_STATIC|SRCHFLG_HTML);
606
+ if( (srchFlags & SRCH_DOC)!=0 ){
607
+ char *zDocGlob = db_get("doc-glob","");
608
+ char *zDocBr = db_get("doc-branch","trunk");
609
+ if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
610
+ db_multi_exec(
611
+ "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
612
+ );
613
+ db_multi_exec(
614
+ "INSERT INTO x(label,url,score,date,snip)"
615
+ " SELECT printf('Document: %%s',foci.filename),"
616
+ " printf('/doc/%T/%%s',foci.filename),"
617
+ " search_score(),"
618
+ " (SELECT datetime(event.mtime) FROM event"
619
+ " WHERE objid=symbolic_name_to_rid('trunk')),"
620
+ " search_snippet()"
621
+ " FROM foci CROSS JOIN blob"
622
+ " WHERE checkinID=symbolic_name_to_rid('trunk')"
623
+ " AND blob.uuid=foci.uuid"
624
+ " AND search_match(stext('d',blob.rid,foci.filename))"
625
+ " AND %z",
626
+ zDocBr, glob_expr("foci.filename", zDocGlob)
627
+ );
628
+ }
629
+ }
630
+ if( (srchFlags & SRCH_WIKI)!=0 ){
631
+ db_multi_exec(
632
+ "WITH wiki(name,rid,mtime) AS ("
633
+ " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
634
+ " FROM tag, tagxref"
635
+ " WHERE tag.tagname GLOB 'wiki-*'"
636
+ " AND tagxref.tagid=tag.tagid"
637
+ " GROUP BY 1"
638
+ ")"
639
+ "INSERT INTO x(label,url,score,date,snip)"
640
+ " SELECT printf('Wiki: %%s',name),"
641
+ " printf('/wiki?name=%%s',urlencode(name)),"
642
+ " search_score(),"
643
+ " datetime(mtime),"
644
+ " search_snippet()"
645
+ " FROM wiki"
646
+ " WHERE search_match(stext('w',rid,name));"
647
+ );
648
+ }
649
+ if( (srchFlags & SRCH_CKIN)!=0 ){
650
+ db_multi_exec(
651
+ "WITH ckin(uuid,rid,mtime) AS ("
652
+ " SELECT blob.uuid, event.objid, event.mtime"
653
+ " FROM event, blob"
654
+ " WHERE event.type='ci'"
655
+ " AND blob.rid=event.objid"
656
+ ")"
657
+ "INSERT INTO x(label,url,score,date,snip)"
658
+ " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
659
+ " printf('/timeline?c=%%s&n=8&y=ci',uuid),"
660
+ " search_score(),"
661
+ " datetime(mtime),"
662
+ " search_snippet()"
663
+ " FROM ckin"
664
+ " WHERE search_match(stext('c',rid,NULL));"
665
+ );
666
+ }
667
+ if( (srchFlags & SRCH_TKT)!=0 ){
668
+ db_multi_exec(
669
+ "INSERT INTO x(label,url,score, date,snip)"
670
+ " SELECT printf('Ticket [%%.17s] on %%s',"
671
+ "tkt_uuid,datetime(tkt_mtime)),"
672
+ " printf('/tktview/%%.20s',tkt_uuid),"
673
+ " search_score(),"
674
+ " datetime(tkt_mtime),"
675
+ " search_snippet()"
676
+ " FROM ticket"
677
+ " WHERE search_match(stext('t',tkt_id,NULL));"
678
+ );
679
+ }
680
+}
681
+
682
+/*
683
+** Implemenation of the rank() function used with rank(matchinfo(*,'pcsx')).
684
+*/
685
+static void search_rank_sqlfunc(
686
+ sqlite3_context *context,
687
+ int argc,
688
+ sqlite3_value **argv
689
+){
690
+ const unsigned *aVal = (unsigned int*)sqlite3_value_blob(argv[0]);
691
+ int nVal = sqlite3_value_bytes(argv[0])/4;
692
+ int nTerm; /* Number of search terms in the query */
693
+ int i; /* Loop counter */
694
+ double r = 1.0; /* Score */
695
+
696
+ if( nVal<6 ) return;
697
+ if( aVal[1]!=1 ) return;
698
+ nTerm = aVal[0];
699
+ r *= 1<<((30*(aVal[2]-1))/nTerm);
700
+ for(i=1; i<=nTerm; i++){
701
+ int hits_this_row = aVal[3*i];
702
+ int hits_all_rows = aVal[3*i+1];
703
+ int rows_with_hit = aVal[3*i+2];
704
+ double avg_hits_per_row = (double)hits_all_rows/(double)rows_with_hit;
705
+ r *= hits_this_row/avg_hits_per_row;
706
+ }
707
+#define SEARCH_DEBUG_RANK 0
708
+#if SEARCH_DEBUG_RANK
709
+ {
710
+ Blob x;
711
+ blob_init(&x,0,0);
712
+ blob_appendf(&x,"%08x", (int)r);
713
+ for(i=0; i<nVal; i++){
714
+ blob_appendf(&x," %d", aVal[i]);
715
+ }
716
+ blob_appendf(&x," r=%g", r);
717
+ sqlite3_result_text(context, blob_str(&x), -1, fossil_free);
718
+ }
719
+#else
720
+ sqlite3_result_double(context, r);
721
+#endif
722
+}
723
+
724
+/*
725
+** When this routine is called, there already exists a table
726
+**
727
+** x(label,url,score,date,snip).
728
+**
729
+** And the srchFlags parameter has been validated. This routine
730
+** fills the X table with search results using a index scan.
731
+**
732
+** The companion full-text scan routine is search_fullscan().
733
+*/
734
+static void search_indexed(
735
+ const char *zPattern, /* The query pattern */
736
+ unsigned int srchFlags /* What to search over */
737
+){
738
+ Blob sql;
739
+ if( srchFlags==0 ) return;
740
+ sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8, 0,
741
+ search_rank_sqlfunc, 0, 0);
742
+ blob_init(&sql, 0, 0);
743
+ blob_appendf(&sql,
744
+ "INSERT INTO x(label,url,score,date,snip) "
745
+ " SELECT ftsdocs.label,"
746
+ " ftsdocs.url,"
747
+ " rank(matchinfo(ftsidx,'pcsx')),"
748
+ " datetime(ftsdocs.mtime),"
749
+ " snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)"
750
+ " FROM ftsidx CROSS JOIN ftsdocs"
751
+ " WHERE ftsidx MATCH %Q"
752
+ " AND ftsdocs.rowid=ftsidx.docid",
753
+ zPattern
754
+ );
755
+ if( srchFlags!=SRCH_ALL ){
756
+ const char *zSep = " AND (";
757
+ static const struct { unsigned m; char c; } aMask[] = {
758
+ { SRCH_CKIN, 'c' },
759
+ { SRCH_DOC, 'd' },
760
+ { SRCH_TKT, 't' },
761
+ { SRCH_WIKI, 'w' },
762
+ };
763
+ int i;
764
+ for(i=0; i<ArraySize(aMask); i++){
765
+ if( srchFlags & aMask[i].m ){
766
+ blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
767
+ zSep = " OR ";
768
+ }
769
+ }
770
+ blob_append(&sql,")",1);
771
+ }
772
+ db_multi_exec("%s",blob_str(&sql)/*safe-for-%s*/);
773
+#if SEARCH_DEBUG_RANK
774
+ db_multi_exec("UPDATE x SET label=printf('%%s (score=%%s)',label,score)");
775
+#endif
776
+}
777
+
778
+/*
779
+** If z[] is of the form "<mark>TEXT</mark>" where TEXT contains
780
+** no white-space or punctuation, then return the length of the mark.
781
+*/
782
+static int isSnippetMark(const char *z){
783
+ int n;
784
+ if( strncmp(z,"<mark>",6)!=0 ) return 0;
785
+ n = 6;
786
+ while( fossil_isalnum(z[n]) ) n++;
787
+ if( strncmp(&z[n],"</mark>",7)!=0 ) return 0;
788
+ return n+7;
789
+}
790
+
791
+/*
792
+** Return a copy of zSnip (in memory obtained from fossil_malloc()) that
793
+** has all "<" characters, other than those on <mark> and </mark>,
794
+** converted into "&lt;". This is similar to htmlize() except that
795
+** <mark> and </mark> are preserved.
796
+*/
797
+static char *cleanSnippet(const char *zSnip){
798
+ int i;
799
+ int n = 0;
800
+ char *z;
801
+ for(i=0; zSnip[i]; i++) if( zSnip[i]=='<' ) n++;
802
+ z = fossil_malloc( i+n*4+1 );
803
+ i = 0;
804
+ while( zSnip[0] ){
805
+ if( zSnip[0]=='<' ){
806
+ n = isSnippetMark(zSnip);
807
+ if( n ){
808
+ memcpy(&z[i], zSnip, n);
809
+ zSnip += n;
810
+ i += n;
811
+ continue;
812
+ }else{
813
+ memcpy(&z[i], "&lt;", 4);
814
+ i += 4;
815
+ zSnip++;
816
+ }
817
+ }else{
818
+ z[i++] = zSnip[0];
819
+ zSnip++;
820
+ }
821
+ }
822
+ z[i] = 0;
823
+ return z;
824
+}
825
+
826
+
827
+/*
828
+** This routine generates web-page output for a search operation.
829
+** Other web-pages can invoke this routine to add search results
830
+** in the middle of the page.
831
+**
832
+** Return the number of rows.
833
+*/
834
+int search_run_and_output(
835
+ const char *zPattern, /* The query pattern */
836
+ unsigned int srchFlags /* What to search over */
837
+){
838
+ Stmt q;
839
+ int nRow = 0;
840
+
841
+ srchFlags = search_restrict(srchFlags);
842
+ if( srchFlags==0 ) return 0;
843
+ search_sql_setup(g.db);
844
+ add_content_sql_commands(g.db);
845
+ db_multi_exec(
846
+ "CREATE TEMP TABLE x(label,url,score,date,snip);"
847
+ );
848
+ if( !search_index_exists() ){
849
+ search_fullscan(zPattern, srchFlags);
850
+ }else{
851
+ search_update_index(srchFlags);
852
+ search_indexed(zPattern, srchFlags);
853
+ }
854
+ db_prepare(&q, "SELECT url, snip, label"
855
+ " FROM x"
856
+ " ORDER BY score DESC, date DESC;");
857
+ while( db_step(&q)==SQLITE_ROW ){
858
+ const char *zUrl = db_column_text(&q, 0);
859
+ const char *zSnippet = db_column_text(&q, 1);
860
+ const char *zLabel = db_column_text(&q, 2);
861
+ if( nRow==0 ){
862
+ @ <ol>
863
+ }
864
+ nRow++;
865
+ @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a><br>
866
+ @ <span class='snippet'>%z(cleanSnippet(zSnippet))</span></li>
867
+ }
868
+ db_finalize(&q);
869
+ if( nRow ){
870
+ @ </ol>
871
+ }
872
+ return nRow;
873
+}
874
+
875
+/*
876
+** Generate some HTML for doing search. At a minimum include the
877
+** Search-Text entry form. If the "s" query parameter is present, also
878
+** show search results.
879
+**
880
+** The srchFlags parameter is used to customize some of the text of the
881
+** form and the results. srchFlags should be either a single search
882
+** category or all categories. Any srchFlags with two or more bits set
883
+** is treated like SRCH_ALL for display purposes.
884
+**
885
+** The entry box is shown disabled if srchFlags is 0.
886
+*/
887
+void search_screen(unsigned srchFlags, const char *zAction){
888
+ const char *zType = 0;
889
+ const char *zClass = 0;
890
+ const char *zDisable1;
891
+ const char *zDisable2;
892
+ const char *zPattern;
893
+ switch( srchFlags ){
894
+ case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break;
895
+ case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break;
896
+ case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break;
897
+ case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break;
898
+ }
899
+ srchFlags = search_restrict(srchFlags);
900
+ if( srchFlags==0 ){
901
+ zDisable1 = " disabled";
902
+ zDisable2 = " disabled";
903
+ zPattern = "";
904
+ }else{
905
+ zDisable1 = " autofocus";
906
+ zDisable2 = "";
907
+ zPattern = PD("s","");
908
+ }
909
+ @ <form method='GET' action='%s(zAction)'>
910
+ if( zClass ){
911
+ @ <div class='searchForm searchForm%s(zClass)'>
912
+ }else{
913
+ @ <div class='searchForm'>
914
+ }
915
+ @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)>
916
+ @ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
917
+ if( srchFlags==0 ){
918
+ @ <p class="generalError">Search is disabled</p>
919
+ }
920
+ @ </div></form>
921
+ while( fossil_isspace(zPattern[0]) ) zPattern++;
922
+ if( zPattern[0] ){
923
+ if( zClass ){
924
+ @ <div class='searchResult searchResult%s(zClass)'>
925
+ }else{
926
+ @ <div class='searchResult'>
927
+ }
928
+ if( search_run_and_output(zPattern, srchFlags)==0 ){
929
+ @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
930
+ }
931
+ @ </div>
932
+ }
933
+}
934
+
935
+/*
936
+** WEBPAGE: /search
937
+**
938
+** Search for check-in comments, documents, tickets, or wiki that
939
+** match a user-supplied pattern.
940
+*/
941
+void search_page(void){
942
+ unsigned srchFlags = SRCH_ALL;
943
+ const char *zOnly = P("only");
944
+
945
+ login_check_credentials();
946
+ if( zOnly ){
947
+ if( strchr(zOnly,'c') ) srchFlags &= SRCH_CKIN;
948
+ if( strchr(zOnly,'d') ) srchFlags &= SRCH_DOC;
949
+ if( strchr(zOnly,'t') ) srchFlags &= SRCH_TKT;
950
+ if( strchr(zOnly,'w') ) srchFlags &= SRCH_WIKI;
951
+ }
952
+ style_header("Search");
953
+ search_screen(srchFlags, "search");
954
+ style_footer();
955
+}
956
+
957
+
958
+/*
959
+** This is a helper function for search_stext(). Writing into pOut
960
+** the search text obtained from pIn according to zMimetype.
961
+*/
962
+static void get_stext_by_mimetype(
963
+ Blob *pIn,
964
+ const char *zMimetype,
965
+ Blob *pOut
966
+){
967
+ Blob html, title;
968
+ blob_init(&html, 0, 0);
969
+ blob_init(&title, 0, 0);
970
+ if( zMimetype==0 ) zMimetype = "text/plain";
971
+ if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
972
+ wiki_convert(pIn, &html, 0);
973
+ html_to_plaintext(blob_str(&html), pOut);
974
+ }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
975
+ markdown_to_html(pIn, &title, &html);
976
+ html_to_plaintext(blob_str(&html), pOut);
977
+ }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
978
+ html_to_plaintext(blob_str(pIn), pOut);
979
+ }else{
980
+ *pOut = *pIn;
981
+ blob_init(pIn, 0, 0);
982
+ }
983
+ blob_reset(&html);
984
+ blob_reset(&title);
985
+}
986
+
987
+/*
988
+** Query pQuery is pointing at a single row of output. Append a text
989
+** representation of every text-compatible column to pAccum.
990
+*/
991
+static void append_all_ticket_fields(Blob *pAccum, Stmt *pQuery){
992
+ int n = db_column_count(pQuery);
993
+ int i;
994
+ for(i=0; i<n; i++){
995
+ const char *zColName = db_column_name(pQuery,i);
996
+ if( fossil_strnicmp(zColName,"tkt_",4)==0 ) continue;
997
+ if( fossil_stricmp(zColName,"mimetype")==0 ) continue;
998
+ switch( db_column_type(pQuery,i) ){
999
+ case SQLITE_INTEGER:
1000
+ case SQLITE_FLOAT:
1001
+ case SQLITE_TEXT:
1002
+ blob_appendf(pAccum, "%s: %s |\n", zColName, db_column_text(pQuery,i));
1003
+ }
1004
+ }
1005
+}
1006
+
1007
+
1008
+/*
1009
+** Return "search text" - a reduced version of a document appropriate for
1010
+** full text search and/or for constructing a search result snippet.
1011
+**
1012
+** cType: d Embedded documentation
1013
+** w Wiki page
1014
+** c Check-in comment
1015
+** t Ticket text
1016
+**
1017
+** rid The RID of an artifact that defines the object
1018
+** being searched.
1019
+**
1020
+** zName Name of the object being searched.
1021
+*/
1022
+void search_stext(
1023
+ char cType, /* Type of document */
1024
+ int rid, /* BLOB.RID or TAG.TAGID value for document */
1025
+ const char *zName, /* Auxiliary information */
1026
+ Blob *pOut /* OUT: Initialize to the search text */
1027
+){
1028
+ blob_init(pOut, 0, 0);
1029
+ switch( cType ){
1030
+ case 'd': { /* Documents */
1031
+ Blob doc;
1032
+ content_get(rid, &doc);
1033
+ blob_to_utf8_no_bom(&doc, 0);
1034
+ get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut);
1035
+ blob_reset(&doc);
1036
+ break;
1037
+ }
1038
+ case 'w': { /* Wiki */
1039
+ Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0);
1040
+ Blob wiki;
1041
+ if( pWiki==0 ) break;
1042
+ blob_init(&wiki, pWiki->zWiki, -1);
1043
+ get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype),
1044
+ pOut);
1045
+ blob_reset(&wiki);
1046
+ manifest_destroy(pWiki);
1047
+ break;
1048
+ }
1049
+ case 'c': { /* Check-in Comments */
1050
+ static Stmt q;
1051
+ db_static_prepare(&q,
1052
+ "SELECT coalesce(ecomment,comment)"
1053
+ " ||' (user: '||coalesce(euser,user,'?')"
1054
+ " ||', tags: '||"
1055
+ " (SELECT group_concat(substr(tag.tagname,5),',')"
1056
+ " FROM tag, tagxref"
1057
+ " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1058
+ " AND tagxref.rid=event.objid AND tagxref.tagtype>0)"
1059
+ " ||')'"
1060
+ " FROM event WHERE objid=:x AND type='ci'");
1061
+ db_bind_int(&q, ":x", rid);
1062
+ if( db_step(&q)==SQLITE_ROW ){
1063
+ db_column_blob(&q, 0, pOut);
1064
+ blob_append(pOut, "\n", 1);
1065
+ }
1066
+ db_reset(&q);
1067
+ break;
1068
+ }
1069
+ case 't': { /* Tickets */
1070
+ static Stmt q1;
1071
+ Blob raw;
1072
+ db_static_prepare(&q1, "SELECT * FROM ticket WHERE tkt_id=:rid");
1073
+ blob_init(&raw,0,0);
1074
+ db_bind_int(&q1, ":rid", rid);
1075
+ if( db_step(&q1)==SQLITE_ROW ){
1076
+ append_all_ticket_fields(&raw, &q1);
1077
+ }
1078
+ db_reset(&q1);
1079
+ if( db_table_exists("repository","ticketchng") ){
1080
+ static Stmt q2;
1081
+ db_static_prepare(&q2, "SELECT * FROM ticketchng WHERE tkt_id=:rid"
1082
+ " ORDER BY tkt_mtime");
1083
+ db_bind_int(&q2, ":rid", rid);
1084
+ while( db_step(&q2)==SQLITE_ROW ){
1085
+ append_all_ticket_fields(&raw, &q2);
1086
+ }
1087
+ db_reset(&q2);
1088
+ }
1089
+ html_to_plaintext(blob_str(&raw), pOut);
1090
+ blob_reset(&raw);
1091
+ break;
1092
+ }
1093
+ }
1094
+}
1095
+
1096
+/*
1097
+** COMMAND: test-search-stext
1098
+**
1099
+** Usage: fossil test-search-stext TYPE ARG1 ARG2
1100
+*/
1101
+void test_search_stext(void){
1102
+ Blob out;
1103
+ db_find_and_open_repository(0,0);
1104
+ if( g.argc!=5 ) usage("TYPE RID NAME");
1105
+ search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out);
1106
+ fossil_print("%s\n",blob_str(&out));
1107
+ blob_reset(&out);
1108
+}
1109
+
1110
+/* The schema for the full-text index
1111
+*/
1112
+static const char zFtsSchema[] =
1113
+@ -- One entry for each possible search result
1114
+@ CREATE TABLE IF NOT EXISTS "%w".ftsdocs(
1115
+@ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.docid
1116
+@ type CHAR(1), -- Type of document
1117
+@ rid INTEGER, -- BLOB.RID or TAG.TAGID for the document
1118
+@ name TEXT, -- Additional document description
1119
+@ idxed BOOLEAN, -- True if currently in the index
1120
+@ label TEXT, -- Label to print on search results
1121
+@ url TEXT, -- URL to access this document
1122
+@ mtime DATE, -- Date when document created
1123
+@ UNIQUE(type,rid)
1124
+@ );
1125
+@ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;
1126
+@ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w';
1127
+@ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS
1128
+@ SELECT rowid, type, rid, name, idxed, label, url, mtime,
1129
+@ stext(type,rid,name) AS 'stext'
1130
+@ FROM ftsdocs;
1131
+@ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx
1132
+@ USING fts4(content="ftscontent", stext);
1133
+;
1134
+static const char zFtsDrop[] =
1135
+@ DROP TABLE IF EXISTS "%w".ftsidx;
1136
+@ DROP VIEW IF EXISTS "%w".ftscontent;
1137
+@ DROP TABLE IF EXISTS "%w".ftsdocs;
1138
+;
1139
+
1140
+/*
1141
+** Create or drop the tables associated with a full-text index.
1142
+*/
1143
+static int searchIdxExists = -1;
1144
+void search_create_index(void){
1145
+ const char *zDb = db_name("repository");
1146
+ search_sql_setup(g.db);
1147
+ db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w"*/,
1148
+ zDb, zDb, zDb, zDb, zDb);
1149
+ searchIdxExists = 1;
1150
+}
1151
+void search_drop_index(void){
1152
+ const char *zDb = db_name("repository");
1153
+ db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb);
1154
+ searchIdxExists = 0;
1155
+}
1156
+
1157
+/*
1158
+** Return true if the full-text search index exists
1159
+*/
1160
+int search_index_exists(void){
1161
+ if( searchIdxExists<0 ){
1162
+ searchIdxExists = db_table_exists("repository","ftsdocs");
1163
+ }
1164
+ return searchIdxExists;
1165
+}
1166
+
1167
+/*
1168
+** Fill the FTSDOCS table with unindexed entries for everything
1169
+** in the repository. This uses INSERT OR IGNORE so entries already
1170
+** in FTSDOCS are unchanged.
1171
+*/
1172
+void search_fill_index(void){
1173
+ if( !search_index_exists() ) return;
1174
+ search_sql_setup(g.db);
1175
+ db_multi_exec(
1176
+ "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
1177
+ " SELECT 'c', objid, 0 FROM event WHERE type='ci';"
1178
+ );
1179
+ db_multi_exec(
1180
+ "WITH latest_wiki(rid,name,mtime) AS ("
1181
+ " SELECT tagxref.rid, substr(tag.tagname,6), max(tagxref.mtime)"
1182
+ " FROM tag, tagxref"
1183
+ " WHERE tag.tagname GLOB 'wiki-*'"
1184
+ " AND tagxref.tagid=tag.tagid"
1185
+ " AND tagxref.value>0"
1186
+ " GROUP BY 2"
1187
+ ") INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)"
1188
+ " SELECT 'w', rid, name, 0 FROM latest_wiki;"
1189
+ );
1190
+ db_multi_exec(
1191
+ "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
1192
+ " SELECT 't', tkt_id, 0 FROM ticket;"
1193
+ );
1194
+}
1195
+
1196
+/*
1197
+** The document described by cType,rid,zName is about to be added or
1198
+** updated. If the document has already been indexed, then unindex it
1199
+** now while we still have access to the old content. Add the document
1200
+** to the queue of documents that need to be indexed or reindexed.
1201
+*/
1202
+void search_doc_touch(char cType, int rid, const char *zName){
1203
+ if( search_index_exists() ){
1204
+ char zType[2];
1205
+ zType[0] = cType;
1206
+ zType[1] = 0;
1207
+ search_sql_setup(g.db);
1208
+ db_multi_exec(
1209
+ "DELETE FROM ftsidx WHERE docid IN"
1210
+ " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)",
1211
+ zType, rid
1212
+ );
1213
+ db_multi_exec(
1214
+ "REPLACE INTO ftsdocs(type,rid,name,idxed)"
1215
+ " VALUES(%Q,%d,%Q,0)",
1216
+ zType, rid, zName
1217
+ );
1218
+ if( cType=='w' ){
1219
+ db_multi_exec(
1220
+ "DELETE FROM ftsidx WHERE docid IN"
1221
+ " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)",
1222
+ zName
1223
+ );
1224
+ db_multi_exec(
1225
+ "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d",
1226
+ zName, rid
1227
+ );
1228
+ }
1229
+ }
1230
+}
1231
+
1232
+/*
1233
+** If the doc-glob and doc-br settings are valid for document search
1234
+** and if the latest check-in on doc-br is in the unindexed set of
1235
+** check-ins, then update all 'd' entries in FTSDOCS that have
1236
+** changed.
1237
+*/
1238
+static void search_update_doc_index(void){
1239
+ const char *zDocBr = db_get("doc-branch","trunk");
1240
+ int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0;
1241
+ double rTime;
1242
+ char *zBrUuid;
1243
+ if( ckid==0 ) return;
1244
+ if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d"
1245
+ " AND NOT idxed", ckid) ) return;
1246
+
1247
+ /* If we get this far, it means that changes to 'd' entries are
1248
+ ** required. */
1249
+ rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid);
1250
+ zBrUuid = db_text("","SELECT substr(uuid,1,20) FROM blob WHERE rid=%d",ckid);
1251
+ db_multi_exec(
1252
+ "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);"
1253
+ "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
1254
+ "INSERT OR IGNORE INTO current_docs(rid, name)"
1255
+ " SELECT blob.rid, foci.filename FROM foci, blob"
1256
+ " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
1257
+ " AND %z",
1258
+ ckid, glob_expr("foci.filename", db_get("doc-glob",""))
1259
+ );
1260
+ db_multi_exec(
1261
+ "DELETE FROM ftsidx WHERE docid IN"
1262
+ " (SELECT rowid FROM ftsdocs WHERE type='d'"
1263
+ " AND rid NOT IN (SELECT rid FROM current_docs))"
1264
+ );
1265
+ db_multi_exec(
1266
+ "DELETE FROM ftsdocs WHERE type='d'"
1267
+ " AND rid NOT IN (SELECT rid FROM current_docs)"
1268
+ );
1269
+ db_multi_exec(
1270
+ "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,url,mtime)"
1271
+ " SELECT 'd', rid, name, 0,"
1272
+ " printf('Document: %%s',name),"
1273
+ " printf('/doc/%q/%%s',urlencode(name)),"
1274
+ " %.17g"
1275
+ " FROM current_docs",
1276
+ zBrUuid, rTime
1277
+ );
1278
+ db_multi_exec(
1279
+ "INSERT INTO ftsidx(docid,stext)"
1280
+ " SELECT rowid, stext FROM ftscontent WHERE type='d' AND NOT idxed"
1281
+ );
1282
+ db_multi_exec(
1283
+ "UPDATE ftsdocs SET idxed=1 WHERE type='d' AND NOT idxed"
1284
+ );
1285
+}
1286
+
1287
+/*
1288
+** Deal with all of the unindexed 'c' terms in FTSDOCS
1289
+*/
1290
+static void search_update_checkin_index(void){
1291
+ db_multi_exec(
1292
+ "INSERT INTO ftsidx(docid,stext)"
1293
+ " SELECT rowid, stext('c',rid,NULL) FROM ftsdocs"
1294
+ " WHERE type='c' AND NOT idxed;"
1295
+ );
1296
+ db_multi_exec(
1297
+ "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1298
+ " SELECT ftsdocs.rowid, 1, 'c', ftsdocs.rid, NULL,"
1299
+ " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime)),"
1300
+ " printf('/timeline?y=ci&n=9&c=%%.20s',blob.uuid),"
1301
+ " event.mtime"
1302
+ " FROM ftsdocs, event, blob"
1303
+ " WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed"
1304
+ " AND event.objid=ftsdocs.rid"
1305
+ " AND blob.rid=ftsdocs.rid"
1306
+ );
1307
+}
1308
+
1309
+/*
1310
+** Deal with all of the unindexed 't' terms in FTSDOCS
1311
+*/
1312
+static void search_update_ticket_index(void){
1313
+ db_multi_exec(
1314
+ "INSERT INTO ftsidx(docid,stext)"
1315
+ " SELECT rowid, stext('t',rid,NULL) FROM ftsdocs"
1316
+ " WHERE type='t' AND NOT idxed;"
1317
+ );
1318
+ if( db_changes()==0 ) return;
1319
+ db_multi_exec(
1320
+ "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1321
+ " SELECT ftsdocs.rowid, 1, 't', ftsdocs.rid, NULL,"
1322
+ " printf('Ticket [%%.16s] on %%s',tkt_uuid,datetime(tkt_mtime)),"
1323
+ " printf('/tktview/%%.20s',tkt_uuid),"
1324
+ " tkt_mtime"
1325
+ " FROM ftsdocs, ticket"
1326
+ " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed"
1327
+ " AND ticket.tkt_id=ftsdocs.rid"
1328
+ );
1329
+}
1330
+
1331
+/*
1332
+** Deal with all of the unindexed 'w' terms in FTSDOCS
1333
+*/
1334
+static void search_update_wiki_index(void){
1335
+ db_multi_exec(
1336
+ "INSERT INTO ftsidx(docid,stext)"
1337
+ " SELECT rowid, stext('w',rid,NULL) FROM ftsdocs"
1338
+ " WHERE type='w' AND NOT idxed;"
1339
+ );
1340
+ if( db_changes()==0 ) return;
1341
+ db_multi_exec(
1342
+ "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1343
+ " SELECT ftsdocs.rowid, 1, 'w', ftsdocs.rid, ftsdocs.name,"
1344
+ " 'Wiki: '||ftsdocs.name,"
1345
+ " '/wiki?name='||urlencode(ftsdocs.name),"
1346
+ " tagxref.mtime"
1347
+ " FROM ftsdocs, tagxref"
1348
+ " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed"
1349
+ " AND tagxref.rid=ftsdocs.rid"
1350
+ );
1351
+}
1352
+
1353
+/*
1354
+** Deal with all of the unindexed entries in the FTSDOCS table - that
1355
+** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the
1356
+** index.
1357
+*/
1358
+void search_update_index(unsigned int srchFlags){
1359
+ if( !search_index_exists() ) return;
1360
+ if( !db_exists("SELECT 1 FROM ftsdocs WHERE NOT idxed") ) return;
1361
+ search_sql_setup(g.db);
1362
+ if( srchFlags & (SRCH_CKIN|SRCH_DOC) ){
1363
+ search_update_doc_index();
1364
+ search_update_checkin_index();
1365
+ }
1366
+ if( srchFlags & SRCH_TKT ){
1367
+ search_update_ticket_index();
1368
+ }
1369
+ if( srchFlags & SRCH_WIKI ){
1370
+ search_update_wiki_index();
1371
+ }
1372
+}
1373
+
1374
+/*
1375
+** Construct, prepopulate, and then update the full-text index.
1376
+*/
1377
+void search_rebuild_index(void){
1378
+ fossil_print("rebuilding the search index...");
1379
+ fflush(stdout);
1380
+ search_create_index();
1381
+ search_fill_index();
1382
+ search_update_index(search_restrict(SRCH_ALL));
1383
+ fossil_print(" done\n");
1384
+}
1385
+
1386
+/*
1387
+** COMMAND: fts-config*
1388
+**
1389
+** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
1390
+**
1391
+** The "fossil fts-config" command configures the full-text search capabilities
1392
+** of the repository. Subcommands:
1393
+**
1394
+** reindex Rebuild the search index. Create it if it does
1395
+** not already exist
1396
+**
1397
+** index (on|off) Turn the search index on or off
1398
+**
1399
+** enable cdtw Enable various kinds of search. c=Check-ins,
1400
+** d=Documents, t=Tickets, w=Wiki.
1401
+**
1402
+** disable cdtw Disable versious kinds of search
1403
+**
1404
+** The current search settings are displayed after any changes are applied.
1405
+** Run this command with no arguments to simply see the settings.
1406
+*/
1407
+void test_fts_cmd(void){
1408
+ static const struct { int iCmd; const char *z; } aCmd[] = {
1409
+ { 1, "reindex" },
1410
+ { 2, "index" },
1411
+ { 3, "disable" },
1412
+ { 4, "enable" },
1413
+ };
1414
+ static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = {
1415
+ { "search-ckin", "check-in search:", "c" },
1416
+ { "search-doc", "document search:", "d" },
1417
+ { "search-tkt", "ticket search:", "t" },
1418
+ { "search-wiki", "wiki search:", "w" },
1419
+ };
1420
+ char *zSubCmd;
1421
+ int i, j, n;
1422
+ int iCmd = 0;
1423
+ int iAction = 0;
1424
+ db_find_and_open_repository(0, 0);
1425
+ if( g.argc>2 ){
1426
+ zSubCmd = g.argv[2];
1427
+ n = (int)strlen(zSubCmd);
1428
+ for(i=0; i<ArraySize(aCmd); i++){
1429
+ if( fossil_strncmp(aCmd[i].z, zSubCmd, n)==0 ) break;
1430
+ }
1431
+ if( i>=ArraySize(aCmd) ){
1432
+ Blob all;
1433
+ blob_init(&all,0,0);
1434
+ for(i=0; i<ArraySize(aCmd); i++) blob_appendf(&all, " %s", aCmd[i].z);
1435
+ fossil_fatal("unknown \"%s\" - should be on of:%s",
1436
+ zSubCmd, blob_str(&all));
1437
+ return;
1438
+ }
1439
+ iCmd = aCmd[i].iCmd;
1440
+ }
1441
+ if( iCmd==1 ){
1442
+ iAction = 2;
1443
+ }
1444
+ if( iCmd==2 ){
1445
+ if( g.argc<3 ) usage("index (on|off)");
1446
+ iAction = 1 + is_truth(g.argv[3]);
1447
+ }
1448
+ db_begin_transaction();
1449
+
1450
+ /* Adjust search settings */
1451
+ if( iCmd==3 || iCmd==4 ){
1452
+ const char *zCtrl;
1453
+ if( g.argc<4 ) usage("enable STRING");
1454
+ zCtrl = g.argv[3];
1455
+ for(j=0; j<ArraySize(aSetng); j++){
1456
+ if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
1457
+ db_set_int(aSetng[j].zSetting, iCmd-3, 0);
1458
+ }
1459
+ }
1460
+ }
1461
+
1462
+ /* destroy or rebuild the index, if requested */
1463
+ if( iAction>=1 ){
1464
+ search_drop_index();
1465
+ }
1466
+ if( iAction>=2 ){
1467
+ search_rebuild_index();
1468
+ }
1469
+
1470
+ /* Always show the status before ending */
1471
+ for(i=0; i<ArraySize(aSetng); i++){
1472
+ fossil_print("%-16s %s\n", aSetng[i].zName,
1473
+ db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
1474
+ }
1475
+ if( search_index_exists() ){
1476
+ fossil_print("%-16s enabled\n", "full-text index:");
1477
+ fossil_print("%-16s %d\n", "documents:",
1478
+ db_int(0, "SELECT count(*) FROM ftsdocs"));
1479
+ }else{
1480
+ fossil_print("%-16s disabled\n", "full-text index:");
1481
+ }
1482
+ db_end_transaction(0);
1483
+}
4191484
--- src/search.c
+++ src/search.c
@@ -40,20 +40,29 @@
40 struct srchTerm { /* For each search term */
41 char *z; /* Text */
42 int n; /* length */
43 } a[SEARCH_MAX_TERM];
44 /* Snippet controls */
45 char *zMarkBegin; /* Start of a match */
 
46 char *zMarkEnd; /* End of a match */
47 char *zMarkGap; /* A gap between two matches */
48 unsigned fSrchFlg; /* Flags */
 
 
49 };
50
51 #define SRCHFLG_HTML 0x0001 /* Escape snippet text for HTML */
 
52
53 #endif
54
 
 
 
 
 
55
56 /*
57 ** Theses characters constitute a word boundary
58 */
59 static const char isBoundary[] = {
@@ -74,23 +83,53 @@
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 };
77 #define ISALNUM(x) (!isBoundary[(x)&0xff])
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79 /*
80 ** Compile a search pattern
81 */
82 Search *search_init(const char *zPattern){
83 int nPattern = strlen(zPattern);
 
 
 
 
 
84 Search *p;
85 char *z;
86 int i;
87
88 p = fossil_malloc( nPattern + sizeof(*p) + 1);
89 z = (char*)&p[1];
90 memcpy(z, zPattern, nPattern+1);
91 memset(p, 0, sizeof(*p));
 
 
 
 
 
 
 
 
 
92 while( *z && p->nTerm<SEARCH_MAX_TERM ){
93 while( *z && !ISALNUM(*z) ){ z++; }
94 if( *z==0 ) break;
95 p->a[p->nTerm].z = z;
96 for(i=1; ISALNUM(z[i]); i++){}
@@ -100,49 +139,46 @@
100 }
101 return p;
102 }
103
104
105 /*
106 ** Destroy a search context.
107 */
108 void search_end(Search *p){
109 free(p);
110 }
111
112 /*
113 ** Append n bytes of text to snippet zTxt. Encode the text appropriately.
114 */
115 static void snippet_text_append(
116 Search *p, /* The search context */
117 Blob *pSnip, /* Append to this snippet */
118 const char *zTxt, /* Text to append */
119 int n /* How many bytes to append */
120 ){
121 if( p->fSrchFlg & SRCHFLG_HTML ){
122 blob_appendf(pSnip, "%.*h", n, zTxt);
123 }else{
124 blob_append(pSnip, zTxt, n);
 
 
125 }
126 }
127
128 /*
129 ** Compare a search pattern against one or more input strings which
130 ** collectively comprise a document. Return a match score. Optionally
131 ** also return a "snippet".
 
 
 
132 **
133 ** Scoring:
134 ** * All terms must match at least once or the score is zero
135 ** * One point for each matching term
136 ** * Extra points if consecutive words of the pattern are consecutive
137 ** in the document
138 */
139 static int search_score(
140 Search *p, /* Search pattern and flags */
141 int nDoc, /* Number of strings in this document */
142 const char **azDoc, /* Text of each string */
143 Blob *pSnip /* If not NULL: Write a snippet here */
144 ){
145 int score; /* Final score */
146 int i; /* Offset into current document */
147 int ii; /* Loop counter */
148 int j; /* Loop over search terms */
@@ -186,25 +222,26 @@
186 }
187 break;
188 }
189 }
190 while( ISALNUM(zDoc[i]) ){ i++; }
 
191 }
192 }
193
194 /* Finished search all documents.
195 ** Every term must be seen or else the score is zero
196 */
197 score = 1;
198 for(j=0; j<p->nTerm; j++) score *= anMatch[j];
199 if( score==0 || pSnip==0 ) return score;
 
 
200
201
202 /* Prepare a snippet that describes the matching text.
203 */
204 blob_init(pSnip, 0, 0);
205
206 while(1){
207 int iOfst;
208 int iTail;
209 int iBest;
210 for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){}
@@ -239,11 +276,11 @@
239 if( iOfst<0 ) iOfst = 0;
240 while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--;
241 while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++;
242 for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){}
243 while( ISALNUM(zDoc[iTail]) ) iTail++;
244 if( iOfst>0 || wantGap ) blob_append(pSnip, p->zMarkGap, -1);
245 wantGap = zDoc[iTail]!=0;
246 zDoc += iOfst;
247 iTail -= iOfst;
248
249 /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */
@@ -252,94 +289,196 @@
252 for(j=0; j<p->nTerm; j++){
253 int n = p->a[j].n;
254 if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0
255 && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*')
256 ){
257 snippet_text_append(p, pSnip, zDoc, i);
258 zDoc += i;
259 iTail -= i;
260 blob_append(pSnip, p->zMarkBegin, -1);
261 if( p->a[j].z[n]=='*' ){
262 while( ISALNUM(zDoc[n]) ) n++;
263 }
264 snippet_text_append(p, pSnip, zDoc, n);
265 zDoc += n;
266 iTail -= n;
267 blob_append(pSnip, p->zMarkEnd, -1);
268 i = -1;
269 break;
270 } /* end-if */
271 } /* end for(j) */
272 if( j<p->nTerm ){
273 while( ISALNUM(zDoc[i]) && i<iTail ){ i++; }
274 }
275 } /* end for(i) */
276 if( iTail>0 ) snippet_text_append(p, pSnip, zDoc, iTail);
277 }
278 if( wantGap ) blob_append(pSnip, p->zMarkGap, -1);
279 return score;
280 }
281
282 /*
283 ** COMMAND: test-snippet
284 **
285 ** Usage: fossil test-snippet SEARCHSTRING FILE1 FILE2 ...
286 */
287 void test_snippet_cmd(void){
288 Search *p;
289 int i;
290 Blob x;
291 Blob snip;
292 int score;
293 char *zDoc;
 
 
 
 
 
 
 
294 if( g.argc<4 ) usage("SEARCHSTRING FILE1...");
295 p = search_init(g.argv[2]);
296 p->zMarkBegin = "[[";
297 p->zMarkEnd = "]]";
298 p->zMarkGap = " ... ";
299 for(i=3; i<g.argc; i++){
300 blob_read_from_file(&x, g.argv[i]);
301 zDoc = blob_str(&x);
302 score = search_score(p, 1, (const char**)&zDoc, &snip);
303 fossil_print("%s: %d\n", g.argv[i], score);
304 blob_reset(&x);
305 if( score ){
306 fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&snip), '=');
307 blob_reset(&snip);
308 }
309 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310 }
311
312 /*
313 ** This is an SQLite function that scores its input using
314 ** a pre-computed pattern.
315 */
316 static void search_score_sqlfunc(
317 sqlite3_context *context,
318 int argc,
319 sqlite3_value **argv
320 ){
321 Search *p = (Search*)sqlite3_user_data(context);
322 const char **azDoc;
323 int score;
324 int i;
325
326 azDoc = fossil_malloc( sizeof(const char*)*(argc+1) );
327 for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]);
328 score = search_score(p, argc, azDoc, 0);
329 fossil_free((void *)azDoc);
330 sqlite3_result_int(context, score);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331 }
332
333 /*
334 ** Register the "score()" SQL function to score its input text
335 ** using the given Search object. Once this function is registered,
336 ** do not delete the Search object.
337 */
338 void search_sql_setup(Search *p){
339 sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p,
 
 
 
 
340 search_score_sqlfunc, 0, 0);
 
 
 
 
 
 
 
 
341 }
342
343 /*
344 ** Testing the search function.
345 **
@@ -357,11 +496,10 @@
357 ** of entries returned. The -width option can be
358 ** used to set the output width used when printing
359 ** matches.
360 */
361 void search_cmd(void){
362 Search *p;
363 Blob pattern;
364 int i;
365 Blob sql = empty_blob;
366 Stmt q;
367 int iBest;
@@ -386,13 +524,13 @@
386 if( g.argc<2 ) return;
387 blob_init(&pattern, g.argv[2], -1);
388 for(i=3; i<g.argc; i++){
389 blob_appendf(&pattern, " %s", g.argv[i]);
390 }
391 p = search_init(blob_str(&pattern));
392 blob_reset(&pattern);
393 search_sql_setup(p);
394
395 db_multi_exec(
396 "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
397 "CREATE INDEX srch_idx1 ON srch(x);"
398 "INSERT INTO srch(rid,uuid,date,comment,x)"
@@ -414,5 +552,932 @@
414 db_prepare(&q, "%s", blob_sql_text(&sql));
415 blob_reset(&sql);
416 print_timeline(&q, nLimit, width, 0);
417 db_finalize(&q);
418 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
--- src/search.c
+++ src/search.c
@@ -40,20 +40,29 @@
40 struct srchTerm { /* For each search term */
41 char *z; /* Text */
42 int n; /* length */
43 } a[SEARCH_MAX_TERM];
44 /* Snippet controls */
45 char *zPattern; /* The search pattern */
46 char *zMarkBegin; /* Start of a match */
47 char *zMarkEnd; /* End of a match */
48 char *zMarkGap; /* A gap between two matches */
49 unsigned fSrchFlg; /* Flags */
50 int iScore; /* Score of the last match attempt */
51 Blob snip; /* Snippet for the most recent match */
52 };
53
54 #define SRCHFLG_HTML 0x01 /* Escape snippet text for HTML */
55 #define SRCHFLG_STATIC 0x04 /* The static gSearch object */
56
57 #endif
58
59 /*
60 ** There is a single global Search object:
61 */
62 static Search gSearch;
63
64
65 /*
66 ** Theses characters constitute a word boundary
67 */
68 static const char isBoundary[] = {
@@ -74,23 +83,53 @@
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 };
86 #define ISALNUM(x) (!isBoundary[(x)&0xff])
87
88
89 /*
90 ** Destroy a search context.
91 */
92 void search_end(Search *p){
93 if( p ){
94 fossil_free(p->zPattern);
95 fossil_free(p->zMarkBegin);
96 fossil_free(p->zMarkEnd);
97 fossil_free(p->zMarkGap);
98 if( p->iScore ) blob_reset(&p->snip);
99 memset(p, 0, sizeof(*p));
100 if( p!=&gSearch ) fossil_free(p);
101 }
102 }
103
104 /*
105 ** Compile a search pattern
106 */
107 Search *search_init(
108 const char *zPattern, /* The search pattern */
109 const char *zMarkBegin, /* Start of a match */
110 const char *zMarkEnd, /* End of a match */
111 const char *zMarkGap, /* A gap between two matches */
112 unsigned fSrchFlg /* Flags */
113 ){
114 Search *p;
115 char *z;
116 int i;
117
118 if( fSrchFlg & SRCHFLG_STATIC ){
119 p = &gSearch;
120 search_end(p);
121 }else{
122 p = fossil_malloc(sizeof(*p));
123 memset(p, 0, sizeof(*p));
124 }
125 p->zPattern = z = mprintf("%s", zPattern);
126 p->zMarkBegin = mprintf("%s", zMarkBegin);
127 p->zMarkEnd = mprintf("%s", zMarkEnd);
128 p->zMarkGap = mprintf("%s", zMarkGap);
129 p->fSrchFlg = fSrchFlg;
130 blob_init(&p->snip, 0, 0);
131 while( *z && p->nTerm<SEARCH_MAX_TERM ){
132 while( *z && !ISALNUM(*z) ){ z++; }
133 if( *z==0 ) break;
134 p->a[p->nTerm].z = z;
135 for(i=1; ISALNUM(z[i]); i++){}
@@ -100,49 +139,46 @@
139 }
140 return p;
141 }
142
143
 
 
 
 
 
 
 
144 /*
145 ** Append n bytes of text to snippet zTxt. Encode the text appropriately.
146 */
147 static void snippet_text_append(
148 Search *p, /* The search context */
149 Blob *pSnip, /* Append to this snippet */
150 const char *zTxt, /* Text to append */
151 int n /* How many bytes to append */
152 ){
153 if( n>0 ){
154 if( p->fSrchFlg & SRCHFLG_HTML ){
155 blob_appendf(pSnip, "%#h", n, zTxt);
156 }else{
157 blob_append(pSnip, zTxt, n);
158 }
159 }
160 }
161
162 /*
163 ** Compare a search pattern against one or more input strings which
164 ** collectively comprise a document. Return a match score. Any
165 ** postive value means there was a match. Zero means that one or
166 ** more terms are missing.
167 **
168 ** The score and a snippet are record for future use.
169 **
170 ** Scoring:
171 ** * All terms must match at least once or the score is zero
172 ** * One point for each matching term
173 ** * Extra points if consecutive words of the pattern are consecutive
174 ** in the document
175 */
176 static int search_match(
177 Search *p, /* Search pattern and flags */
178 int nDoc, /* Number of strings in this document */
179 const char **azDoc /* Text of each string */
 
180 ){
181 int score; /* Final score */
182 int i; /* Offset into current document */
183 int ii; /* Loop counter */
184 int j; /* Loop over search terms */
@@ -186,25 +222,26 @@
222 }
223 break;
224 }
225 }
226 while( ISALNUM(zDoc[i]) ){ i++; }
227 if( zDoc[i]==0 ) break;
228 }
229 }
230
231 /* Finished search all documents.
232 ** Every term must be seen or else the score is zero
233 */
234 score = 1;
235 for(j=0; j<p->nTerm; j++) score *= anMatch[j];
236 blob_reset(&p->snip);
237 p->iScore = score;
238 if( score==0 ) return score;
239
240
241 /* Prepare a snippet that describes the matching text.
242 */
 
 
243 while(1){
244 int iOfst;
245 int iTail;
246 int iBest;
247 for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){}
@@ -239,11 +276,11 @@
276 if( iOfst<0 ) iOfst = 0;
277 while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--;
278 while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++;
279 for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){}
280 while( ISALNUM(zDoc[iTail]) ) iTail++;
281 if( iOfst>0 || wantGap ) blob_append(&p->snip, p->zMarkGap, -1);
282 wantGap = zDoc[iTail]!=0;
283 zDoc += iOfst;
284 iTail -= iOfst;
285
286 /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */
@@ -252,94 +289,196 @@
289 for(j=0; j<p->nTerm; j++){
290 int n = p->a[j].n;
291 if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0
292 && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*')
293 ){
294 snippet_text_append(p, &p->snip, zDoc, i);
295 zDoc += i;
296 iTail -= i;
297 blob_append(&p->snip, p->zMarkBegin, -1);
298 if( p->a[j].z[n]=='*' ){
299 while( ISALNUM(zDoc[n]) ) n++;
300 }
301 snippet_text_append(p, &p->snip, zDoc, n);
302 zDoc += n;
303 iTail -= n;
304 blob_append(&p->snip, p->zMarkEnd, -1);
305 i = -1;
306 break;
307 } /* end-if */
308 } /* end for(j) */
309 if( j<p->nTerm ){
310 while( ISALNUM(zDoc[i]) && i<iTail ){ i++; }
311 }
312 } /* end for(i) */
313 snippet_text_append(p, &p->snip, zDoc, iTail);
314 }
315 if( wantGap ) blob_append(&p->snip, p->zMarkGap, -1);
316 return score;
317 }
318
319 /*
320 ** COMMAND: test-match
321 **
322 ** Usage: fossil test-match SEARCHSTRING FILE1 FILE2 ...
323 */
324 void test_match_cmd(void){
325 Search *p;
326 int i;
327 Blob x;
 
328 int score;
329 char *zDoc;
330 int flg = 0;
331 char *zBegin = (char*)find_option("begin",0,1);
332 char *zEnd = (char*)find_option("end",0,1);
333 char *zGap = (char*)find_option("gap",0,1);
334 if( find_option("html",0,0)!=0 ) flg |= SRCHFLG_HTML;
335 if( find_option("static",0,0)!=0 ) flg |= SRCHFLG_STATIC;
336 verify_all_options();
337 if( g.argc<4 ) usage("SEARCHSTRING FILE1...");
338 if( zBegin==0 ) zBegin = "[[";
339 if( zEnd==0 ) zEnd = "]]";
340 if( zGap==0 ) zGap = " ... ";
341 p = search_init(g.argv[2], zBegin, zEnd, zGap, flg);
342 for(i=3; i<g.argc; i++){
343 blob_read_from_file(&x, g.argv[i]);
344 zDoc = blob_str(&x);
345 score = search_match(p, 1, (const char**)&zDoc);
346 fossil_print("%s: %d\n", g.argv[i], p->iScore);
347 blob_reset(&x);
348 if( score ){
349 fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&p->snip), '=');
 
350 }
351 }
352 search_end(p);
353 }
354
355 /*
356 ** An SQL function to initialize the global search pattern:
357 **
358 ** search_init(PATTERN,BEGIN,END,GAP,FLAGS)
359 **
360 ** All arguments are optional.
361 */
362 static void search_init_sqlfunc(
363 sqlite3_context *context,
364 int argc,
365 sqlite3_value **argv
366 ){
367 const char *zPattern = 0;
368 const char *zBegin = "<mark>";
369 const char *zEnd = "</mark>";
370 const char *zGap = " ... ";
371 unsigned int flg = SRCHFLG_HTML;
372 switch( argc ){
373 default:
374 flg = (unsigned int)sqlite3_value_int(argv[4]);
375 case 4:
376 zGap = (const char*)sqlite3_value_text(argv[3]);
377 case 3:
378 zEnd = (const char*)sqlite3_value_text(argv[2]);
379 case 2:
380 zBegin = (const char*)sqlite3_value_text(argv[1]);
381 case 1:
382 zPattern = (const char*)sqlite3_value_text(argv[0]);
383 }
384 if( zPattern && zPattern[0] ){
385 search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC);
386 }else{
387 search_end(&gSearch);
388 }
389 }
390
391 /*
392 ** Try to match the input text against the search parameters set up
393 ** by the previous search_init() call. Remember the results globally.
394 ** Return non-zero on a match and zero on a miss.
395 */
396 static void search_match_sqlfunc(
397 sqlite3_context *context,
398 int argc,
399 sqlite3_value **argv
400 ){
401 const char *zSText = (const char*)sqlite3_value_text(argv[0]);
402 int rc;
403 if( zSText==0 ) return;
404 rc = search_match(&gSearch, 1, &zSText);
405 sqlite3_result_int(context, rc);
406 }
407
408 /*
409 ** These SQL functions return the results of the last
410 ** call to the search_match() SQL function.
411 */
412 static void search_score_sqlfunc(
413 sqlite3_context *context,
414 int argc,
415 sqlite3_value **argv
416 ){
417 sqlite3_result_int(context, gSearch.iScore);
418 }
419 static void search_snippet_sqlfunc(
420 sqlite3_context *context,
421 int argc,
422 sqlite3_value **argv
423 ){
424 if( blob_size(&gSearch.snip)>0 ){
425 sqlite3_result_text(context, blob_str(&gSearch.snip), -1, fossil_free);
426 blob_init(&gSearch.snip, 0, 0);
427 }
428 }
429
430 /*
431 ** This is an SQLite function that computes the searchable text.
432 ** It is a wrapper around the search_stext() routine. See the
433 ** search_stext() routine for further detail.
434 */
435 static void search_stext_sqlfunc(
436 sqlite3_context *context,
437 int argc,
438 sqlite3_value **argv
439 ){
440 Blob txt;
441 const char *zType = (const char*)sqlite3_value_text(argv[0]);
442 int rid = sqlite3_value_int(argv[1]);
443 const char *zName = (const char*)sqlite3_value_text(argv[2]);
444 search_stext(zType[0], rid, zName, &txt);
445 sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free);
446 }
447
448 /*
449 ** Encode a string for use as a query parameter in a URL
450 */
451 static void search_urlencode_sqlfunc(
452 sqlite3_context *context,
453 int argc,
454 sqlite3_value **argv
455 ){
456 char *z = mprintf("%T",sqlite3_value_text(argv[0]));
457 sqlite3_result_text(context, z, -1, fossil_free);
458 }
459
460 /*
461 ** Register the "score()" SQL function to score its input text
462 ** using the given Search object. Once this function is registered,
463 ** do not delete the Search object.
464 */
465 void search_sql_setup(sqlite3 *db){
466 static int once = 0;
467 if( once++ ) return;
468 sqlite3_create_function(db, "search_match", 1, SQLITE_UTF8, 0,
469 search_match_sqlfunc, 0, 0);
470 sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0,
471 search_score_sqlfunc, 0, 0);
472 sqlite3_create_function(db, "search_snippet", 0, SQLITE_UTF8, 0,
473 search_snippet_sqlfunc, 0, 0);
474 sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0,
475 search_init_sqlfunc, 0, 0);
476 sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0,
477 search_stext_sqlfunc, 0, 0);
478 sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0,
479 search_urlencode_sqlfunc, 0, 0);
480 }
481
482 /*
483 ** Testing the search function.
484 **
@@ -357,11 +496,10 @@
496 ** of entries returned. The -width option can be
497 ** used to set the output width used when printing
498 ** matches.
499 */
500 void search_cmd(void){
 
501 Blob pattern;
502 int i;
503 Blob sql = empty_blob;
504 Stmt q;
505 int iBest;
@@ -386,13 +524,13 @@
524 if( g.argc<2 ) return;
525 blob_init(&pattern, g.argv[2], -1);
526 for(i=3; i<g.argc; i++){
527 blob_appendf(&pattern, " %s", g.argv[i]);
528 }
529 (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
530 blob_reset(&pattern);
531 search_sql_setup(g.db);
532
533 db_multi_exec(
534 "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
535 "CREATE INDEX srch_idx1 ON srch(x);"
536 "INSERT INTO srch(rid,uuid,date,comment,x)"
@@ -414,5 +552,932 @@
552 db_prepare(&q, "%s", blob_sql_text(&sql));
553 blob_reset(&sql);
554 print_timeline(&q, nLimit, width, 0);
555 db_finalize(&q);
556 }
557
558 #if INTERFACE
559 /* What to search for */
560 #define SRCH_CKIN 0x0001 /* Search over check-in comments */
561 #define SRCH_DOC 0x0002 /* Search over embedded documents */
562 #define SRCH_TKT 0x0004 /* Search over tickets */
563 #define SRCH_WIKI 0x0008 /* Search over wiki */
564 #define SRCH_ALL 0x000f /* Search over everything */
565 #endif
566
567 /*
568 ** Remove bits from srchFlags which are disallowed by either the
569 ** current server configuration or by user permissions.
570 */
571 unsigned int search_restrict(unsigned int srchFlags){
572 if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC);
573 if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT);
574 if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
575 if( (srchFlags & SRCH_CKIN)!=0 && db_get_boolean("search-ci",0)==0 ){
576 srchFlags &= ~SRCH_CKIN;
577 }
578 if( (srchFlags & SRCH_DOC)!=0 && db_get_boolean("search-doc",0)==0 ){
579 srchFlags &= ~SRCH_DOC;
580 }
581 if( (srchFlags & SRCH_TKT)!=0 && db_get_boolean("search-tkt",0)==0 ){
582 srchFlags &= ~SRCH_TKT;
583 }
584 if( (srchFlags & SRCH_WIKI)!=0 && db_get_boolean("search-wiki",0)==0 ){
585 srchFlags &= ~SRCH_WIKI;
586 }
587 return srchFlags;
588 }
589
590 /*
591 ** When this routine is called, there already exists a table
592 **
593 ** x(label,url,score,date,snip).
594 **
595 ** And the srchFlags parameter has been validated. This routine
596 ** fills the X table with search results using a full-text scan.
597 **
598 ** The companion indexed scan routine is search_indexed().
599 */
600 static void search_fullscan(
601 const char *zPattern, /* The query pattern */
602 unsigned int srchFlags /* What to search over */
603 ){
604 search_init(zPattern, "<mark>", "</mark>", " ... ",
605 SRCHFLG_STATIC|SRCHFLG_HTML);
606 if( (srchFlags & SRCH_DOC)!=0 ){
607 char *zDocGlob = db_get("doc-glob","");
608 char *zDocBr = db_get("doc-branch","trunk");
609 if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){
610 db_multi_exec(
611 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
612 );
613 db_multi_exec(
614 "INSERT INTO x(label,url,score,date,snip)"
615 " SELECT printf('Document: %%s',foci.filename),"
616 " printf('/doc/%T/%%s',foci.filename),"
617 " search_score(),"
618 " (SELECT datetime(event.mtime) FROM event"
619 " WHERE objid=symbolic_name_to_rid('trunk')),"
620 " search_snippet()"
621 " FROM foci CROSS JOIN blob"
622 " WHERE checkinID=symbolic_name_to_rid('trunk')"
623 " AND blob.uuid=foci.uuid"
624 " AND search_match(stext('d',blob.rid,foci.filename))"
625 " AND %z",
626 zDocBr, glob_expr("foci.filename", zDocGlob)
627 );
628 }
629 }
630 if( (srchFlags & SRCH_WIKI)!=0 ){
631 db_multi_exec(
632 "WITH wiki(name,rid,mtime) AS ("
633 " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)"
634 " FROM tag, tagxref"
635 " WHERE tag.tagname GLOB 'wiki-*'"
636 " AND tagxref.tagid=tag.tagid"
637 " GROUP BY 1"
638 ")"
639 "INSERT INTO x(label,url,score,date,snip)"
640 " SELECT printf('Wiki: %%s',name),"
641 " printf('/wiki?name=%%s',urlencode(name)),"
642 " search_score(),"
643 " datetime(mtime),"
644 " search_snippet()"
645 " FROM wiki"
646 " WHERE search_match(stext('w',rid,name));"
647 );
648 }
649 if( (srchFlags & SRCH_CKIN)!=0 ){
650 db_multi_exec(
651 "WITH ckin(uuid,rid,mtime) AS ("
652 " SELECT blob.uuid, event.objid, event.mtime"
653 " FROM event, blob"
654 " WHERE event.type='ci'"
655 " AND blob.rid=event.objid"
656 ")"
657 "INSERT INTO x(label,url,score,date,snip)"
658 " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime)),"
659 " printf('/timeline?c=%%s&n=8&y=ci',uuid),"
660 " search_score(),"
661 " datetime(mtime),"
662 " search_snippet()"
663 " FROM ckin"
664 " WHERE search_match(stext('c',rid,NULL));"
665 );
666 }
667 if( (srchFlags & SRCH_TKT)!=0 ){
668 db_multi_exec(
669 "INSERT INTO x(label,url,score, date,snip)"
670 " SELECT printf('Ticket [%%.17s] on %%s',"
671 "tkt_uuid,datetime(tkt_mtime)),"
672 " printf('/tktview/%%.20s',tkt_uuid),"
673 " search_score(),"
674 " datetime(tkt_mtime),"
675 " search_snippet()"
676 " FROM ticket"
677 " WHERE search_match(stext('t',tkt_id,NULL));"
678 );
679 }
680 }
681
682 /*
683 ** Implemenation of the rank() function used with rank(matchinfo(*,'pcsx')).
684 */
685 static void search_rank_sqlfunc(
686 sqlite3_context *context,
687 int argc,
688 sqlite3_value **argv
689 ){
690 const unsigned *aVal = (unsigned int*)sqlite3_value_blob(argv[0]);
691 int nVal = sqlite3_value_bytes(argv[0])/4;
692 int nTerm; /* Number of search terms in the query */
693 int i; /* Loop counter */
694 double r = 1.0; /* Score */
695
696 if( nVal<6 ) return;
697 if( aVal[1]!=1 ) return;
698 nTerm = aVal[0];
699 r *= 1<<((30*(aVal[2]-1))/nTerm);
700 for(i=1; i<=nTerm; i++){
701 int hits_this_row = aVal[3*i];
702 int hits_all_rows = aVal[3*i+1];
703 int rows_with_hit = aVal[3*i+2];
704 double avg_hits_per_row = (double)hits_all_rows/(double)rows_with_hit;
705 r *= hits_this_row/avg_hits_per_row;
706 }
707 #define SEARCH_DEBUG_RANK 0
708 #if SEARCH_DEBUG_RANK
709 {
710 Blob x;
711 blob_init(&x,0,0);
712 blob_appendf(&x,"%08x", (int)r);
713 for(i=0; i<nVal; i++){
714 blob_appendf(&x," %d", aVal[i]);
715 }
716 blob_appendf(&x," r=%g", r);
717 sqlite3_result_text(context, blob_str(&x), -1, fossil_free);
718 }
719 #else
720 sqlite3_result_double(context, r);
721 #endif
722 }
723
724 /*
725 ** When this routine is called, there already exists a table
726 **
727 ** x(label,url,score,date,snip).
728 **
729 ** And the srchFlags parameter has been validated. This routine
730 ** fills the X table with search results using a index scan.
731 **
732 ** The companion full-text scan routine is search_fullscan().
733 */
734 static void search_indexed(
735 const char *zPattern, /* The query pattern */
736 unsigned int srchFlags /* What to search over */
737 ){
738 Blob sql;
739 if( srchFlags==0 ) return;
740 sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8, 0,
741 search_rank_sqlfunc, 0, 0);
742 blob_init(&sql, 0, 0);
743 blob_appendf(&sql,
744 "INSERT INTO x(label,url,score,date,snip) "
745 " SELECT ftsdocs.label,"
746 " ftsdocs.url,"
747 " rank(matchinfo(ftsidx,'pcsx')),"
748 " datetime(ftsdocs.mtime),"
749 " snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)"
750 " FROM ftsidx CROSS JOIN ftsdocs"
751 " WHERE ftsidx MATCH %Q"
752 " AND ftsdocs.rowid=ftsidx.docid",
753 zPattern
754 );
755 if( srchFlags!=SRCH_ALL ){
756 const char *zSep = " AND (";
757 static const struct { unsigned m; char c; } aMask[] = {
758 { SRCH_CKIN, 'c' },
759 { SRCH_DOC, 'd' },
760 { SRCH_TKT, 't' },
761 { SRCH_WIKI, 'w' },
762 };
763 int i;
764 for(i=0; i<ArraySize(aMask); i++){
765 if( srchFlags & aMask[i].m ){
766 blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
767 zSep = " OR ";
768 }
769 }
770 blob_append(&sql,")",1);
771 }
772 db_multi_exec("%s",blob_str(&sql)/*safe-for-%s*/);
773 #if SEARCH_DEBUG_RANK
774 db_multi_exec("UPDATE x SET label=printf('%%s (score=%%s)',label,score)");
775 #endif
776 }
777
778 /*
779 ** If z[] is of the form "<mark>TEXT</mark>" where TEXT contains
780 ** no white-space or punctuation, then return the length of the mark.
781 */
782 static int isSnippetMark(const char *z){
783 int n;
784 if( strncmp(z,"<mark>",6)!=0 ) return 0;
785 n = 6;
786 while( fossil_isalnum(z[n]) ) n++;
787 if( strncmp(&z[n],"</mark>",7)!=0 ) return 0;
788 return n+7;
789 }
790
791 /*
792 ** Return a copy of zSnip (in memory obtained from fossil_malloc()) that
793 ** has all "<" characters, other than those on <mark> and </mark>,
794 ** converted into "&lt;". This is similar to htmlize() except that
795 ** <mark> and </mark> are preserved.
796 */
797 static char *cleanSnippet(const char *zSnip){
798 int i;
799 int n = 0;
800 char *z;
801 for(i=0; zSnip[i]; i++) if( zSnip[i]=='<' ) n++;
802 z = fossil_malloc( i+n*4+1 );
803 i = 0;
804 while( zSnip[0] ){
805 if( zSnip[0]=='<' ){
806 n = isSnippetMark(zSnip);
807 if( n ){
808 memcpy(&z[i], zSnip, n);
809 zSnip += n;
810 i += n;
811 continue;
812 }else{
813 memcpy(&z[i], "&lt;", 4);
814 i += 4;
815 zSnip++;
816 }
817 }else{
818 z[i++] = zSnip[0];
819 zSnip++;
820 }
821 }
822 z[i] = 0;
823 return z;
824 }
825
826
827 /*
828 ** This routine generates web-page output for a search operation.
829 ** Other web-pages can invoke this routine to add search results
830 ** in the middle of the page.
831 **
832 ** Return the number of rows.
833 */
834 int search_run_and_output(
835 const char *zPattern, /* The query pattern */
836 unsigned int srchFlags /* What to search over */
837 ){
838 Stmt q;
839 int nRow = 0;
840
841 srchFlags = search_restrict(srchFlags);
842 if( srchFlags==0 ) return 0;
843 search_sql_setup(g.db);
844 add_content_sql_commands(g.db);
845 db_multi_exec(
846 "CREATE TEMP TABLE x(label,url,score,date,snip);"
847 );
848 if( !search_index_exists() ){
849 search_fullscan(zPattern, srchFlags);
850 }else{
851 search_update_index(srchFlags);
852 search_indexed(zPattern, srchFlags);
853 }
854 db_prepare(&q, "SELECT url, snip, label"
855 " FROM x"
856 " ORDER BY score DESC, date DESC;");
857 while( db_step(&q)==SQLITE_ROW ){
858 const char *zUrl = db_column_text(&q, 0);
859 const char *zSnippet = db_column_text(&q, 1);
860 const char *zLabel = db_column_text(&q, 2);
861 if( nRow==0 ){
862 @ <ol>
863 }
864 nRow++;
865 @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a><br>
866 @ <span class='snippet'>%z(cleanSnippet(zSnippet))</span></li>
867 }
868 db_finalize(&q);
869 if( nRow ){
870 @ </ol>
871 }
872 return nRow;
873 }
874
875 /*
876 ** Generate some HTML for doing search. At a minimum include the
877 ** Search-Text entry form. If the "s" query parameter is present, also
878 ** show search results.
879 **
880 ** The srchFlags parameter is used to customize some of the text of the
881 ** form and the results. srchFlags should be either a single search
882 ** category or all categories. Any srchFlags with two or more bits set
883 ** is treated like SRCH_ALL for display purposes.
884 **
885 ** The entry box is shown disabled if srchFlags is 0.
886 */
887 void search_screen(unsigned srchFlags, const char *zAction){
888 const char *zType = 0;
889 const char *zClass = 0;
890 const char *zDisable1;
891 const char *zDisable2;
892 const char *zPattern;
893 switch( srchFlags ){
894 case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break;
895 case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break;
896 case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break;
897 case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break;
898 }
899 srchFlags = search_restrict(srchFlags);
900 if( srchFlags==0 ){
901 zDisable1 = " disabled";
902 zDisable2 = " disabled";
903 zPattern = "";
904 }else{
905 zDisable1 = " autofocus";
906 zDisable2 = "";
907 zPattern = PD("s","");
908 }
909 @ <form method='GET' action='%s(zAction)'>
910 if( zClass ){
911 @ <div class='searchForm searchForm%s(zClass)'>
912 }else{
913 @ <div class='searchForm'>
914 }
915 @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)>
916 @ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
917 if( srchFlags==0 ){
918 @ <p class="generalError">Search is disabled</p>
919 }
920 @ </div></form>
921 while( fossil_isspace(zPattern[0]) ) zPattern++;
922 if( zPattern[0] ){
923 if( zClass ){
924 @ <div class='searchResult searchResult%s(zClass)'>
925 }else{
926 @ <div class='searchResult'>
927 }
928 if( search_run_and_output(zPattern, srchFlags)==0 ){
929 @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
930 }
931 @ </div>
932 }
933 }
934
935 /*
936 ** WEBPAGE: /search
937 **
938 ** Search for check-in comments, documents, tickets, or wiki that
939 ** match a user-supplied pattern.
940 */
941 void search_page(void){
942 unsigned srchFlags = SRCH_ALL;
943 const char *zOnly = P("only");
944
945 login_check_credentials();
946 if( zOnly ){
947 if( strchr(zOnly,'c') ) srchFlags &= SRCH_CKIN;
948 if( strchr(zOnly,'d') ) srchFlags &= SRCH_DOC;
949 if( strchr(zOnly,'t') ) srchFlags &= SRCH_TKT;
950 if( strchr(zOnly,'w') ) srchFlags &= SRCH_WIKI;
951 }
952 style_header("Search");
953 search_screen(srchFlags, "search");
954 style_footer();
955 }
956
957
958 /*
959 ** This is a helper function for search_stext(). Writing into pOut
960 ** the search text obtained from pIn according to zMimetype.
961 */
962 static void get_stext_by_mimetype(
963 Blob *pIn,
964 const char *zMimetype,
965 Blob *pOut
966 ){
967 Blob html, title;
968 blob_init(&html, 0, 0);
969 blob_init(&title, 0, 0);
970 if( zMimetype==0 ) zMimetype = "text/plain";
971 if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
972 wiki_convert(pIn, &html, 0);
973 html_to_plaintext(blob_str(&html), pOut);
974 }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
975 markdown_to_html(pIn, &title, &html);
976 html_to_plaintext(blob_str(&html), pOut);
977 }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
978 html_to_plaintext(blob_str(pIn), pOut);
979 }else{
980 *pOut = *pIn;
981 blob_init(pIn, 0, 0);
982 }
983 blob_reset(&html);
984 blob_reset(&title);
985 }
986
987 /*
988 ** Query pQuery is pointing at a single row of output. Append a text
989 ** representation of every text-compatible column to pAccum.
990 */
991 static void append_all_ticket_fields(Blob *pAccum, Stmt *pQuery){
992 int n = db_column_count(pQuery);
993 int i;
994 for(i=0; i<n; i++){
995 const char *zColName = db_column_name(pQuery,i);
996 if( fossil_strnicmp(zColName,"tkt_",4)==0 ) continue;
997 if( fossil_stricmp(zColName,"mimetype")==0 ) continue;
998 switch( db_column_type(pQuery,i) ){
999 case SQLITE_INTEGER:
1000 case SQLITE_FLOAT:
1001 case SQLITE_TEXT:
1002 blob_appendf(pAccum, "%s: %s |\n", zColName, db_column_text(pQuery,i));
1003 }
1004 }
1005 }
1006
1007
1008 /*
1009 ** Return "search text" - a reduced version of a document appropriate for
1010 ** full text search and/or for constructing a search result snippet.
1011 **
1012 ** cType: d Embedded documentation
1013 ** w Wiki page
1014 ** c Check-in comment
1015 ** t Ticket text
1016 **
1017 ** rid The RID of an artifact that defines the object
1018 ** being searched.
1019 **
1020 ** zName Name of the object being searched.
1021 */
1022 void search_stext(
1023 char cType, /* Type of document */
1024 int rid, /* BLOB.RID or TAG.TAGID value for document */
1025 const char *zName, /* Auxiliary information */
1026 Blob *pOut /* OUT: Initialize to the search text */
1027 ){
1028 blob_init(pOut, 0, 0);
1029 switch( cType ){
1030 case 'd': { /* Documents */
1031 Blob doc;
1032 content_get(rid, &doc);
1033 blob_to_utf8_no_bom(&doc, 0);
1034 get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut);
1035 blob_reset(&doc);
1036 break;
1037 }
1038 case 'w': { /* Wiki */
1039 Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0);
1040 Blob wiki;
1041 if( pWiki==0 ) break;
1042 blob_init(&wiki, pWiki->zWiki, -1);
1043 get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype),
1044 pOut);
1045 blob_reset(&wiki);
1046 manifest_destroy(pWiki);
1047 break;
1048 }
1049 case 'c': { /* Check-in Comments */
1050 static Stmt q;
1051 db_static_prepare(&q,
1052 "SELECT coalesce(ecomment,comment)"
1053 " ||' (user: '||coalesce(euser,user,'?')"
1054 " ||', tags: '||"
1055 " (SELECT group_concat(substr(tag.tagname,5),',')"
1056 " FROM tag, tagxref"
1057 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1058 " AND tagxref.rid=event.objid AND tagxref.tagtype>0)"
1059 " ||')'"
1060 " FROM event WHERE objid=:x AND type='ci'");
1061 db_bind_int(&q, ":x", rid);
1062 if( db_step(&q)==SQLITE_ROW ){
1063 db_column_blob(&q, 0, pOut);
1064 blob_append(pOut, "\n", 1);
1065 }
1066 db_reset(&q);
1067 break;
1068 }
1069 case 't': { /* Tickets */
1070 static Stmt q1;
1071 Blob raw;
1072 db_static_prepare(&q1, "SELECT * FROM ticket WHERE tkt_id=:rid");
1073 blob_init(&raw,0,0);
1074 db_bind_int(&q1, ":rid", rid);
1075 if( db_step(&q1)==SQLITE_ROW ){
1076 append_all_ticket_fields(&raw, &q1);
1077 }
1078 db_reset(&q1);
1079 if( db_table_exists("repository","ticketchng") ){
1080 static Stmt q2;
1081 db_static_prepare(&q2, "SELECT * FROM ticketchng WHERE tkt_id=:rid"
1082 " ORDER BY tkt_mtime");
1083 db_bind_int(&q2, ":rid", rid);
1084 while( db_step(&q2)==SQLITE_ROW ){
1085 append_all_ticket_fields(&raw, &q2);
1086 }
1087 db_reset(&q2);
1088 }
1089 html_to_plaintext(blob_str(&raw), pOut);
1090 blob_reset(&raw);
1091 break;
1092 }
1093 }
1094 }
1095
1096 /*
1097 ** COMMAND: test-search-stext
1098 **
1099 ** Usage: fossil test-search-stext TYPE ARG1 ARG2
1100 */
1101 void test_search_stext(void){
1102 Blob out;
1103 db_find_and_open_repository(0,0);
1104 if( g.argc!=5 ) usage("TYPE RID NAME");
1105 search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out);
1106 fossil_print("%s\n",blob_str(&out));
1107 blob_reset(&out);
1108 }
1109
1110 /* The schema for the full-text index
1111 */
1112 static const char zFtsSchema[] =
1113 @ -- One entry for each possible search result
1114 @ CREATE TABLE IF NOT EXISTS "%w".ftsdocs(
1115 @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.docid
1116 @ type CHAR(1), -- Type of document
1117 @ rid INTEGER, -- BLOB.RID or TAG.TAGID for the document
1118 @ name TEXT, -- Additional document description
1119 @ idxed BOOLEAN, -- True if currently in the index
1120 @ label TEXT, -- Label to print on search results
1121 @ url TEXT, -- URL to access this document
1122 @ mtime DATE, -- Date when document created
1123 @ UNIQUE(type,rid)
1124 @ );
1125 @ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;
1126 @ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w';
1127 @ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS
1128 @ SELECT rowid, type, rid, name, idxed, label, url, mtime,
1129 @ stext(type,rid,name) AS 'stext'
1130 @ FROM ftsdocs;
1131 @ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx
1132 @ USING fts4(content="ftscontent", stext);
1133 ;
1134 static const char zFtsDrop[] =
1135 @ DROP TABLE IF EXISTS "%w".ftsidx;
1136 @ DROP VIEW IF EXISTS "%w".ftscontent;
1137 @ DROP TABLE IF EXISTS "%w".ftsdocs;
1138 ;
1139
1140 /*
1141 ** Create or drop the tables associated with a full-text index.
1142 */
1143 static int searchIdxExists = -1;
1144 void search_create_index(void){
1145 const char *zDb = db_name("repository");
1146 search_sql_setup(g.db);
1147 db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w"*/,
1148 zDb, zDb, zDb, zDb, zDb);
1149 searchIdxExists = 1;
1150 }
1151 void search_drop_index(void){
1152 const char *zDb = db_name("repository");
1153 db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb);
1154 searchIdxExists = 0;
1155 }
1156
1157 /*
1158 ** Return true if the full-text search index exists
1159 */
1160 int search_index_exists(void){
1161 if( searchIdxExists<0 ){
1162 searchIdxExists = db_table_exists("repository","ftsdocs");
1163 }
1164 return searchIdxExists;
1165 }
1166
1167 /*
1168 ** Fill the FTSDOCS table with unindexed entries for everything
1169 ** in the repository. This uses INSERT OR IGNORE so entries already
1170 ** in FTSDOCS are unchanged.
1171 */
1172 void search_fill_index(void){
1173 if( !search_index_exists() ) return;
1174 search_sql_setup(g.db);
1175 db_multi_exec(
1176 "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
1177 " SELECT 'c', objid, 0 FROM event WHERE type='ci';"
1178 );
1179 db_multi_exec(
1180 "WITH latest_wiki(rid,name,mtime) AS ("
1181 " SELECT tagxref.rid, substr(tag.tagname,6), max(tagxref.mtime)"
1182 " FROM tag, tagxref"
1183 " WHERE tag.tagname GLOB 'wiki-*'"
1184 " AND tagxref.tagid=tag.tagid"
1185 " AND tagxref.value>0"
1186 " GROUP BY 2"
1187 ") INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)"
1188 " SELECT 'w', rid, name, 0 FROM latest_wiki;"
1189 );
1190 db_multi_exec(
1191 "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)"
1192 " SELECT 't', tkt_id, 0 FROM ticket;"
1193 );
1194 }
1195
1196 /*
1197 ** The document described by cType,rid,zName is about to be added or
1198 ** updated. If the document has already been indexed, then unindex it
1199 ** now while we still have access to the old content. Add the document
1200 ** to the queue of documents that need to be indexed or reindexed.
1201 */
1202 void search_doc_touch(char cType, int rid, const char *zName){
1203 if( search_index_exists() ){
1204 char zType[2];
1205 zType[0] = cType;
1206 zType[1] = 0;
1207 search_sql_setup(g.db);
1208 db_multi_exec(
1209 "DELETE FROM ftsidx WHERE docid IN"
1210 " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)",
1211 zType, rid
1212 );
1213 db_multi_exec(
1214 "REPLACE INTO ftsdocs(type,rid,name,idxed)"
1215 " VALUES(%Q,%d,%Q,0)",
1216 zType, rid, zName
1217 );
1218 if( cType=='w' ){
1219 db_multi_exec(
1220 "DELETE FROM ftsidx WHERE docid IN"
1221 " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)",
1222 zName
1223 );
1224 db_multi_exec(
1225 "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d",
1226 zName, rid
1227 );
1228 }
1229 }
1230 }
1231
1232 /*
1233 ** If the doc-glob and doc-br settings are valid for document search
1234 ** and if the latest check-in on doc-br is in the unindexed set of
1235 ** check-ins, then update all 'd' entries in FTSDOCS that have
1236 ** changed.
1237 */
1238 static void search_update_doc_index(void){
1239 const char *zDocBr = db_get("doc-branch","trunk");
1240 int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0;
1241 double rTime;
1242 char *zBrUuid;
1243 if( ckid==0 ) return;
1244 if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d"
1245 " AND NOT idxed", ckid) ) return;
1246
1247 /* If we get this far, it means that changes to 'd' entries are
1248 ** required. */
1249 rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid);
1250 zBrUuid = db_text("","SELECT substr(uuid,1,20) FROM blob WHERE rid=%d",ckid);
1251 db_multi_exec(
1252 "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);"
1253 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;"
1254 "INSERT OR IGNORE INTO current_docs(rid, name)"
1255 " SELECT blob.rid, foci.filename FROM foci, blob"
1256 " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
1257 " AND %z",
1258 ckid, glob_expr("foci.filename", db_get("doc-glob",""))
1259 );
1260 db_multi_exec(
1261 "DELETE FROM ftsidx WHERE docid IN"
1262 " (SELECT rowid FROM ftsdocs WHERE type='d'"
1263 " AND rid NOT IN (SELECT rid FROM current_docs))"
1264 );
1265 db_multi_exec(
1266 "DELETE FROM ftsdocs WHERE type='d'"
1267 " AND rid NOT IN (SELECT rid FROM current_docs)"
1268 );
1269 db_multi_exec(
1270 "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,url,mtime)"
1271 " SELECT 'd', rid, name, 0,"
1272 " printf('Document: %%s',name),"
1273 " printf('/doc/%q/%%s',urlencode(name)),"
1274 " %.17g"
1275 " FROM current_docs",
1276 zBrUuid, rTime
1277 );
1278 db_multi_exec(
1279 "INSERT INTO ftsidx(docid,stext)"
1280 " SELECT rowid, stext FROM ftscontent WHERE type='d' AND NOT idxed"
1281 );
1282 db_multi_exec(
1283 "UPDATE ftsdocs SET idxed=1 WHERE type='d' AND NOT idxed"
1284 );
1285 }
1286
1287 /*
1288 ** Deal with all of the unindexed 'c' terms in FTSDOCS
1289 */
1290 static void search_update_checkin_index(void){
1291 db_multi_exec(
1292 "INSERT INTO ftsidx(docid,stext)"
1293 " SELECT rowid, stext('c',rid,NULL) FROM ftsdocs"
1294 " WHERE type='c' AND NOT idxed;"
1295 );
1296 db_multi_exec(
1297 "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1298 " SELECT ftsdocs.rowid, 1, 'c', ftsdocs.rid, NULL,"
1299 " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime)),"
1300 " printf('/timeline?y=ci&n=9&c=%%.20s',blob.uuid),"
1301 " event.mtime"
1302 " FROM ftsdocs, event, blob"
1303 " WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed"
1304 " AND event.objid=ftsdocs.rid"
1305 " AND blob.rid=ftsdocs.rid"
1306 );
1307 }
1308
1309 /*
1310 ** Deal with all of the unindexed 't' terms in FTSDOCS
1311 */
1312 static void search_update_ticket_index(void){
1313 db_multi_exec(
1314 "INSERT INTO ftsidx(docid,stext)"
1315 " SELECT rowid, stext('t',rid,NULL) FROM ftsdocs"
1316 " WHERE type='t' AND NOT idxed;"
1317 );
1318 if( db_changes()==0 ) return;
1319 db_multi_exec(
1320 "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1321 " SELECT ftsdocs.rowid, 1, 't', ftsdocs.rid, NULL,"
1322 " printf('Ticket [%%.16s] on %%s',tkt_uuid,datetime(tkt_mtime)),"
1323 " printf('/tktview/%%.20s',tkt_uuid),"
1324 " tkt_mtime"
1325 " FROM ftsdocs, ticket"
1326 " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed"
1327 " AND ticket.tkt_id=ftsdocs.rid"
1328 );
1329 }
1330
1331 /*
1332 ** Deal with all of the unindexed 'w' terms in FTSDOCS
1333 */
1334 static void search_update_wiki_index(void){
1335 db_multi_exec(
1336 "INSERT INTO ftsidx(docid,stext)"
1337 " SELECT rowid, stext('w',rid,NULL) FROM ftsdocs"
1338 " WHERE type='w' AND NOT idxed;"
1339 );
1340 if( db_changes()==0 ) return;
1341 db_multi_exec(
1342 "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)"
1343 " SELECT ftsdocs.rowid, 1, 'w', ftsdocs.rid, ftsdocs.name,"
1344 " 'Wiki: '||ftsdocs.name,"
1345 " '/wiki?name='||urlencode(ftsdocs.name),"
1346 " tagxref.mtime"
1347 " FROM ftsdocs, tagxref"
1348 " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed"
1349 " AND tagxref.rid=ftsdocs.rid"
1350 );
1351 }
1352
1353 /*
1354 ** Deal with all of the unindexed entries in the FTSDOCS table - that
1355 ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the
1356 ** index.
1357 */
1358 void search_update_index(unsigned int srchFlags){
1359 if( !search_index_exists() ) return;
1360 if( !db_exists("SELECT 1 FROM ftsdocs WHERE NOT idxed") ) return;
1361 search_sql_setup(g.db);
1362 if( srchFlags & (SRCH_CKIN|SRCH_DOC) ){
1363 search_update_doc_index();
1364 search_update_checkin_index();
1365 }
1366 if( srchFlags & SRCH_TKT ){
1367 search_update_ticket_index();
1368 }
1369 if( srchFlags & SRCH_WIKI ){
1370 search_update_wiki_index();
1371 }
1372 }
1373
1374 /*
1375 ** Construct, prepopulate, and then update the full-text index.
1376 */
1377 void search_rebuild_index(void){
1378 fossil_print("rebuilding the search index...");
1379 fflush(stdout);
1380 search_create_index();
1381 search_fill_index();
1382 search_update_index(search_restrict(SRCH_ALL));
1383 fossil_print(" done\n");
1384 }
1385
1386 /*
1387 ** COMMAND: fts-config*
1388 **
1389 ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
1390 **
1391 ** The "fossil fts-config" command configures the full-text search capabilities
1392 ** of the repository. Subcommands:
1393 **
1394 ** reindex Rebuild the search index. Create it if it does
1395 ** not already exist
1396 **
1397 ** index (on|off) Turn the search index on or off
1398 **
1399 ** enable cdtw Enable various kinds of search. c=Check-ins,
1400 ** d=Documents, t=Tickets, w=Wiki.
1401 **
1402 ** disable cdtw Disable versious kinds of search
1403 **
1404 ** The current search settings are displayed after any changes are applied.
1405 ** Run this command with no arguments to simply see the settings.
1406 */
1407 void test_fts_cmd(void){
1408 static const struct { int iCmd; const char *z; } aCmd[] = {
1409 { 1, "reindex" },
1410 { 2, "index" },
1411 { 3, "disable" },
1412 { 4, "enable" },
1413 };
1414 static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = {
1415 { "search-ckin", "check-in search:", "c" },
1416 { "search-doc", "document search:", "d" },
1417 { "search-tkt", "ticket search:", "t" },
1418 { "search-wiki", "wiki search:", "w" },
1419 };
1420 char *zSubCmd;
1421 int i, j, n;
1422 int iCmd = 0;
1423 int iAction = 0;
1424 db_find_and_open_repository(0, 0);
1425 if( g.argc>2 ){
1426 zSubCmd = g.argv[2];
1427 n = (int)strlen(zSubCmd);
1428 for(i=0; i<ArraySize(aCmd); i++){
1429 if( fossil_strncmp(aCmd[i].z, zSubCmd, n)==0 ) break;
1430 }
1431 if( i>=ArraySize(aCmd) ){
1432 Blob all;
1433 blob_init(&all,0,0);
1434 for(i=0; i<ArraySize(aCmd); i++) blob_appendf(&all, " %s", aCmd[i].z);
1435 fossil_fatal("unknown \"%s\" - should be on of:%s",
1436 zSubCmd, blob_str(&all));
1437 return;
1438 }
1439 iCmd = aCmd[i].iCmd;
1440 }
1441 if( iCmd==1 ){
1442 iAction = 2;
1443 }
1444 if( iCmd==2 ){
1445 if( g.argc<3 ) usage("index (on|off)");
1446 iAction = 1 + is_truth(g.argv[3]);
1447 }
1448 db_begin_transaction();
1449
1450 /* Adjust search settings */
1451 if( iCmd==3 || iCmd==4 ){
1452 const char *zCtrl;
1453 if( g.argc<4 ) usage("enable STRING");
1454 zCtrl = g.argv[3];
1455 for(j=0; j<ArraySize(aSetng); j++){
1456 if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
1457 db_set_int(aSetng[j].zSetting, iCmd-3, 0);
1458 }
1459 }
1460 }
1461
1462 /* destroy or rebuild the index, if requested */
1463 if( iAction>=1 ){
1464 search_drop_index();
1465 }
1466 if( iAction>=2 ){
1467 search_rebuild_index();
1468 }
1469
1470 /* Always show the status before ending */
1471 for(i=0; i<ArraySize(aSetng); i++){
1472 fossil_print("%-16s %s\n", aSetng[i].zName,
1473 db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
1474 }
1475 if( search_index_exists() ){
1476 fossil_print("%-16s enabled\n", "full-text index:");
1477 fossil_print("%-16s %d\n", "documents:",
1478 db_int(0, "SELECT count(*) FROM ftsdocs"));
1479 }else{
1480 fossil_print("%-16s disabled\n", "full-text index:");
1481 }
1482 db_end_transaction(0);
1483 }
1484
+82 -8
--- src/setup.c
+++ src/setup.c
@@ -70,11 +70,11 @@
7070
@ <p class="generalError"><b>Configuration Error:</b> Please add
7171
@ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
7272
@ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
7373
}
7474
75
- @ <table border="0" cellspacing="7">
75
+ @ <table border="0" cellspacing="3">
7676
setup_menu_entry("Users", "setup_ulist",
7777
"Grant privileges to individual users.");
7878
setup_menu_entry("Access", "setup_access",
7979
"Control access settings.");
8080
setup_menu_entry("Configuration", "setup_config",
@@ -86,10 +86,12 @@
8686
setup_menu_entry("Login-Group", "setup_login_group",
8787
"Manage single sign-on between this repository and others"
8888
" on the same server");
8989
setup_menu_entry("Tickets", "tktsetup",
9090
"Configure the trouble-ticketing system for this repository");
91
+ setup_menu_entry("Search","srchsetup",
92
+ "Configure the built-in search engine");
9193
setup_menu_entry("Transfers", "xfersetup",
9294
"Configure the transfer system for this repository");
9395
setup_menu_entry("Skins", "setup_skin",
9496
"Select from a menu of prepackaged \"skins\" for the web interface");
9597
setup_menu_entry("CSS", "setup_editcss",
@@ -1354,11 +1356,11 @@
13541356
13551357
/*
13561358
** WEBPAGE: setup_settings
13571359
*/
13581360
void setup_settings(void){
1359
- struct stControlSettings const *pSet;
1361
+ Setting const *pSet;
13601362
13611363
login_check_credentials();
13621364
if( !g.perm.Setup ){
13631365
login_needed();
13641366
}
@@ -1375,14 +1377,14 @@
13751377
@ See the "fossil help setting" output below for further information on
13761378
@ the meaning of each setting.</p><hr />
13771379
@ <form action="%s(g.zTop)/setup_settings" method="post"><div>
13781380
@ <table border="0"><tr><td valign="top">
13791381
login_insert_csrf_secret();
1380
- for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1382
+ for(pSet=aSetting; pSet->name!=0; pSet++){
13811383
if( pSet->width==0 ){
13821384
int hasVersionableValue = pSet->versionable &&
1383
- (db_get_do_versionable(pSet->name, NULL)!=0);
1385
+ (db_get_versioned(pSet->name, NULL)!=0);
13841386
onoff_attribute(pSet->name, pSet->name,
13851387
pSet->var!=0 ? pSet->var : pSet->name,
13861388
is_truth(pSet->def), hasVersionableValue);
13871389
if( pSet->versionable ){
13881390
@ (v)<br />
@@ -1391,31 +1393,31 @@
13911393
}
13921394
}
13931395
}
13941396
@ <br /><input type="submit" name="submit" value="Apply Changes" />
13951397
@ </td><td style="width:50px;"></td><td valign="top">
1396
- for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1398
+ for(pSet=aSetting; pSet->name!=0; pSet++){
13971399
if( pSet->width!=0 && !pSet->versionable && !pSet->forceTextArea ){
13981400
entry_attribute(pSet->name, /*pSet->width*/ 25, pSet->name,
13991401
pSet->var!=0 ? pSet->var : pSet->name,
14001402
(char*)pSet->def, 0);
14011403
@ <br />
14021404
}
14031405
}
1404
- for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1406
+ for(pSet=aSetting; pSet->name!=0; pSet++){
14051407
if( pSet->width!=0 && !pSet->versionable && pSet->forceTextArea ){
14061408
@<b>%s(pSet->name)</b><br />
14071409
textarea_attribute("", /*rows*/ 3, /*cols*/ 50, pSet->name,
14081410
pSet->var!=0 ? pSet->var : pSet->name,
14091411
(char*)pSet->def, 0);
14101412
@ <br />
14111413
}
14121414
}
14131415
@ </td><td style="width:50px;"></td><td valign="top">
1414
- for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1416
+ for(pSet=aSetting; pSet->name!=0; pSet++){
14151417
if( pSet->width!=0 && pSet->versionable ){
1416
- int hasVersionableValue = db_get_do_versionable(pSet->name, NULL)!=0;
1418
+ int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
14171419
@<b>%s(pSet->name)</b> (v)<br />
14181420
textarea_attribute("", /*rows*/ 3, /*cols*/ 20, pSet->name,
14191421
pSet->var!=0 ? pSet->var : pSet->name,
14201422
(char*)pSet->def, hasVersionableValue);
14211423
@<br />
@@ -2155,5 +2157,77 @@
21552157
if(limit>0 && counter<limit){
21562158
@ <div>%d(counter) entries shown.</div>
21572159
}
21582160
style_footer();
21592161
}
2162
+
2163
+/*
2164
+** WEBPAGE: srchsetup
2165
+**
2166
+** Configure the search engine.
2167
+*/
2168
+void page_srchsetup(){
2169
+ login_check_credentials();
2170
+ if( !g.perm.Setup && !g.perm.Admin ){
2171
+ login_needed();
2172
+ }
2173
+ style_header("Search Configuration");
2174
+ @ <form action="%s(g.zTop)/srchsetup" method="post"><div>
2175
+ login_insert_csrf_secret();
2176
+ @ <div style="text-align:center;font-weight:bold;">
2177
+ @ Server-specific settings that affect the
2178
+ @ <a href="%R/search">/search</a> webpage.
2179
+ @ </div>
2180
+ @ <hr />
2181
+ textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
2182
+ @ <p>The "Document Glob List" is a comma- or newline-separated list
2183
+ @ of GLOB expressions that identify all documents within the source
2184
+ @ tree that are to be searched when "Document Search" is enabled.
2185
+ @ Some examples:
2186
+ @ <table border=0 cellpadding=2 align=center>
2187
+ @ <tr><td>*.wiki,*.html,*.md,*.txt<td style="width: 4x;">
2188
+ @ <td>Search all wiki, HTML, Markdown, and Text files</tr>
2189
+ @ <tr><td>doc/*.md,*/README.txt,README.txt<td>
2190
+ @ <td>Search all Markdown files in the doc/ subfolder and all README.txt
2191
+ @ files.</tr>
2192
+ @ <tr><td>*<td><td>Search all checked-in files</tr>
2193
+ @ <tr><td><i>(blank)</i><td>
2194
+ @ <td>Search nothing. (Disables document search).</tr>
2195
+ @ </table>
2196
+ @ <hr />
2197
+ entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
2198
+ @ <p>When searching documents, use the versions of the files found at the
2199
+ @ type of the "Document Branch" branch. Recommended value: "trunk".
2200
+ @ Document search is disabled if blank.
2201
+ @ <hr/>
2202
+ onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
2203
+ @ <br>
2204
+ onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
2205
+ @ <br>
2206
+ onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
2207
+ @ <br>
2208
+ onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0);
2209
+ @ <hr/>
2210
+ @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
2211
+ @ <hr/>
2212
+ if( P("fts0") ){
2213
+ search_drop_index();
2214
+ }else if( P("fts1") ){
2215
+ search_drop_index();
2216
+ search_create_index();
2217
+ search_fill_index();
2218
+ search_update_index(search_restrict(SRCH_ALL));
2219
+ }
2220
+ if( search_index_exists() ){
2221
+ @ <p>Currently using an SQLite FTS4 search index. This makes search
2222
+ @ run faster, especially on large repositories, but takes up space.</p>
2223
+ @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
2224
+ @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
2225
+ }else{
2226
+ @ <p>The SQLite FTS4 search index is disabled. All searching will be
2227
+ @ a full-text scan. This usually works fine, but can be slow for
2228
+ @ larger repositories.</p>
2229
+ @ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
2230
+ }
2231
+ @ </div></form>
2232
+ style_footer();
2233
+}
21602234
--- src/setup.c
+++ src/setup.c
@@ -70,11 +70,11 @@
70 @ <p class="generalError"><b>Configuration Error:</b> Please add
71 @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
72 @ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
73 }
74
75 @ <table border="0" cellspacing="7">
76 setup_menu_entry("Users", "setup_ulist",
77 "Grant privileges to individual users.");
78 setup_menu_entry("Access", "setup_access",
79 "Control access settings.");
80 setup_menu_entry("Configuration", "setup_config",
@@ -86,10 +86,12 @@
86 setup_menu_entry("Login-Group", "setup_login_group",
87 "Manage single sign-on between this repository and others"
88 " on the same server");
89 setup_menu_entry("Tickets", "tktsetup",
90 "Configure the trouble-ticketing system for this repository");
 
 
91 setup_menu_entry("Transfers", "xfersetup",
92 "Configure the transfer system for this repository");
93 setup_menu_entry("Skins", "setup_skin",
94 "Select from a menu of prepackaged \"skins\" for the web interface");
95 setup_menu_entry("CSS", "setup_editcss",
@@ -1354,11 +1356,11 @@
1354
1355 /*
1356 ** WEBPAGE: setup_settings
1357 */
1358 void setup_settings(void){
1359 struct stControlSettings const *pSet;
1360
1361 login_check_credentials();
1362 if( !g.perm.Setup ){
1363 login_needed();
1364 }
@@ -1375,14 +1377,14 @@
1375 @ See the "fossil help setting" output below for further information on
1376 @ the meaning of each setting.</p><hr />
1377 @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
1378 @ <table border="0"><tr><td valign="top">
1379 login_insert_csrf_secret();
1380 for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1381 if( pSet->width==0 ){
1382 int hasVersionableValue = pSet->versionable &&
1383 (db_get_do_versionable(pSet->name, NULL)!=0);
1384 onoff_attribute(pSet->name, pSet->name,
1385 pSet->var!=0 ? pSet->var : pSet->name,
1386 is_truth(pSet->def), hasVersionableValue);
1387 if( pSet->versionable ){
1388 @ (v)<br />
@@ -1391,31 +1393,31 @@
1391 }
1392 }
1393 }
1394 @ <br /><input type="submit" name="submit" value="Apply Changes" />
1395 @ </td><td style="width:50px;"></td><td valign="top">
1396 for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1397 if( pSet->width!=0 && !pSet->versionable && !pSet->forceTextArea ){
1398 entry_attribute(pSet->name, /*pSet->width*/ 25, pSet->name,
1399 pSet->var!=0 ? pSet->var : pSet->name,
1400 (char*)pSet->def, 0);
1401 @ <br />
1402 }
1403 }
1404 for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1405 if( pSet->width!=0 && !pSet->versionable && pSet->forceTextArea ){
1406 @<b>%s(pSet->name)</b><br />
1407 textarea_attribute("", /*rows*/ 3, /*cols*/ 50, pSet->name,
1408 pSet->var!=0 ? pSet->var : pSet->name,
1409 (char*)pSet->def, 0);
1410 @ <br />
1411 }
1412 }
1413 @ </td><td style="width:50px;"></td><td valign="top">
1414 for(pSet=ctrlSettings; pSet->name!=0; pSet++){
1415 if( pSet->width!=0 && pSet->versionable ){
1416 int hasVersionableValue = db_get_do_versionable(pSet->name, NULL)!=0;
1417 @<b>%s(pSet->name)</b> (v)<br />
1418 textarea_attribute("", /*rows*/ 3, /*cols*/ 20, pSet->name,
1419 pSet->var!=0 ? pSet->var : pSet->name,
1420 (char*)pSet->def, hasVersionableValue);
1421 @<br />
@@ -2155,5 +2157,77 @@
2155 if(limit>0 && counter<limit){
2156 @ <div>%d(counter) entries shown.</div>
2157 }
2158 style_footer();
2159 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2160
--- src/setup.c
+++ src/setup.c
@@ -70,11 +70,11 @@
70 @ <p class="generalError"><b>Configuration Error:</b> Please add
71 @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
72 @ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
73 }
74
75 @ <table border="0" cellspacing="3">
76 setup_menu_entry("Users", "setup_ulist",
77 "Grant privileges to individual users.");
78 setup_menu_entry("Access", "setup_access",
79 "Control access settings.");
80 setup_menu_entry("Configuration", "setup_config",
@@ -86,10 +86,12 @@
86 setup_menu_entry("Login-Group", "setup_login_group",
87 "Manage single sign-on between this repository and others"
88 " on the same server");
89 setup_menu_entry("Tickets", "tktsetup",
90 "Configure the trouble-ticketing system for this repository");
91 setup_menu_entry("Search","srchsetup",
92 "Configure the built-in search engine");
93 setup_menu_entry("Transfers", "xfersetup",
94 "Configure the transfer system for this repository");
95 setup_menu_entry("Skins", "setup_skin",
96 "Select from a menu of prepackaged \"skins\" for the web interface");
97 setup_menu_entry("CSS", "setup_editcss",
@@ -1354,11 +1356,11 @@
1356
1357 /*
1358 ** WEBPAGE: setup_settings
1359 */
1360 void setup_settings(void){
1361 Setting const *pSet;
1362
1363 login_check_credentials();
1364 if( !g.perm.Setup ){
1365 login_needed();
1366 }
@@ -1375,14 +1377,14 @@
1377 @ See the "fossil help setting" output below for further information on
1378 @ the meaning of each setting.</p><hr />
1379 @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
1380 @ <table border="0"><tr><td valign="top">
1381 login_insert_csrf_secret();
1382 for(pSet=aSetting; pSet->name!=0; pSet++){
1383 if( pSet->width==0 ){
1384 int hasVersionableValue = pSet->versionable &&
1385 (db_get_versioned(pSet->name, NULL)!=0);
1386 onoff_attribute(pSet->name, pSet->name,
1387 pSet->var!=0 ? pSet->var : pSet->name,
1388 is_truth(pSet->def), hasVersionableValue);
1389 if( pSet->versionable ){
1390 @ (v)<br />
@@ -1391,31 +1393,31 @@
1393 }
1394 }
1395 }
1396 @ <br /><input type="submit" name="submit" value="Apply Changes" />
1397 @ </td><td style="width:50px;"></td><td valign="top">
1398 for(pSet=aSetting; pSet->name!=0; pSet++){
1399 if( pSet->width!=0 && !pSet->versionable && !pSet->forceTextArea ){
1400 entry_attribute(pSet->name, /*pSet->width*/ 25, pSet->name,
1401 pSet->var!=0 ? pSet->var : pSet->name,
1402 (char*)pSet->def, 0);
1403 @ <br />
1404 }
1405 }
1406 for(pSet=aSetting; pSet->name!=0; pSet++){
1407 if( pSet->width!=0 && !pSet->versionable && pSet->forceTextArea ){
1408 @<b>%s(pSet->name)</b><br />
1409 textarea_attribute("", /*rows*/ 3, /*cols*/ 50, pSet->name,
1410 pSet->var!=0 ? pSet->var : pSet->name,
1411 (char*)pSet->def, 0);
1412 @ <br />
1413 }
1414 }
1415 @ </td><td style="width:50px;"></td><td valign="top">
1416 for(pSet=aSetting; pSet->name!=0; pSet++){
1417 if( pSet->width!=0 && pSet->versionable ){
1418 int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
1419 @<b>%s(pSet->name)</b> (v)<br />
1420 textarea_attribute("", /*rows*/ 3, /*cols*/ 20, pSet->name,
1421 pSet->var!=0 ? pSet->var : pSet->name,
1422 (char*)pSet->def, hasVersionableValue);
1423 @<br />
@@ -2155,5 +2157,77 @@
2157 if(limit>0 && counter<limit){
2158 @ <div>%d(counter) entries shown.</div>
2159 }
2160 style_footer();
2161 }
2162
2163 /*
2164 ** WEBPAGE: srchsetup
2165 **
2166 ** Configure the search engine.
2167 */
2168 void page_srchsetup(){
2169 login_check_credentials();
2170 if( !g.perm.Setup && !g.perm.Admin ){
2171 login_needed();
2172 }
2173 style_header("Search Configuration");
2174 @ <form action="%s(g.zTop)/srchsetup" method="post"><div>
2175 login_insert_csrf_secret();
2176 @ <div style="text-align:center;font-weight:bold;">
2177 @ Server-specific settings that affect the
2178 @ <a href="%R/search">/search</a> webpage.
2179 @ </div>
2180 @ <hr />
2181 textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
2182 @ <p>The "Document Glob List" is a comma- or newline-separated list
2183 @ of GLOB expressions that identify all documents within the source
2184 @ tree that are to be searched when "Document Search" is enabled.
2185 @ Some examples:
2186 @ <table border=0 cellpadding=2 align=center>
2187 @ <tr><td>*.wiki,*.html,*.md,*.txt<td style="width: 4x;">
2188 @ <td>Search all wiki, HTML, Markdown, and Text files</tr>
2189 @ <tr><td>doc/*.md,*/README.txt,README.txt<td>
2190 @ <td>Search all Markdown files in the doc/ subfolder and all README.txt
2191 @ files.</tr>
2192 @ <tr><td>*<td><td>Search all checked-in files</tr>
2193 @ <tr><td><i>(blank)</i><td>
2194 @ <td>Search nothing. (Disables document search).</tr>
2195 @ </table>
2196 @ <hr />
2197 entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
2198 @ <p>When searching documents, use the versions of the files found at the
2199 @ type of the "Document Branch" branch. Recommended value: "trunk".
2200 @ Document search is disabled if blank.
2201 @ <hr/>
2202 onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
2203 @ <br>
2204 onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
2205 @ <br>
2206 onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
2207 @ <br>
2208 onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0);
2209 @ <hr/>
2210 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
2211 @ <hr/>
2212 if( P("fts0") ){
2213 search_drop_index();
2214 }else if( P("fts1") ){
2215 search_drop_index();
2216 search_create_index();
2217 search_fill_index();
2218 search_update_index(search_restrict(SRCH_ALL));
2219 }
2220 if( search_index_exists() ){
2221 @ <p>Currently using an SQLite FTS4 search index. This makes search
2222 @ run faster, especially on large repositories, but takes up space.</p>
2223 @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
2224 @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
2225 }else{
2226 @ <p>The SQLite FTS4 search index is disabled. All searching will be
2227 @ a full-text scan. This usually works fine, but can be slow for
2228 @ larger repositories.</p>
2229 @ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
2230 }
2231 @ </div></form>
2232 style_footer();
2233 }
2234
+2 -2
--- src/skins.c
+++ src/skins.c
@@ -26,11 +26,11 @@
2626
**
2727
** To add new built-in skins:
2828
**
2929
** 1. Pick a name for the new skin. (Here we use "xyzzy").
3030
**
31
-** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt,
31
+** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt,
3232
** and skins/xyzzy/footer.txt into the source tree.
3333
**
3434
** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to
3535
** rebuild the makefiles to reference the new CSS, headers, and footers.
3636
**
@@ -42,11 +42,11 @@
4242
char *zSQL; /* Filled in at run-time with SQL to insert this skin */
4343
} aBuiltinSkin[] = {
4444
{ "Default", "default", 0 },
4545
{ "Plain Gray, No Logo", "plain_gray", 0 },
4646
{ "Khaki, No Logo", "khaki", 0 },
47
- { "Black & White, Menu on Left", "black_and_white", 0 },
47
+ { "Black & White, Menu on Left", "black_and_white", 0 },
4848
{ "Shadow boxes & Rounded Corners", "rounded1", 0 },
4949
{ "Enhanced Default", "enhanced1", 0 },
5050
{ "San Francisco Modern", "etienne1", 0 },
5151
{ "Eagle", "eagle", 0 },
5252
};
5353
--- src/skins.c
+++ src/skins.c
@@ -26,11 +26,11 @@
26 **
27 ** To add new built-in skins:
28 **
29 ** 1. Pick a name for the new skin. (Here we use "xyzzy").
30 **
31 ** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt,
32 ** and skins/xyzzy/footer.txt into the source tree.
33 **
34 ** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to
35 ** rebuild the makefiles to reference the new CSS, headers, and footers.
36 **
@@ -42,11 +42,11 @@
42 char *zSQL; /* Filled in at run-time with SQL to insert this skin */
43 } aBuiltinSkin[] = {
44 { "Default", "default", 0 },
45 { "Plain Gray, No Logo", "plain_gray", 0 },
46 { "Khaki, No Logo", "khaki", 0 },
47 { "Black & White, Menu on Left", "black_and_white", 0 },
48 { "Shadow boxes & Rounded Corners", "rounded1", 0 },
49 { "Enhanced Default", "enhanced1", 0 },
50 { "San Francisco Modern", "etienne1", 0 },
51 { "Eagle", "eagle", 0 },
52 };
53
--- src/skins.c
+++ src/skins.c
@@ -26,11 +26,11 @@
26 **
27 ** To add new built-in skins:
28 **
29 ** 1. Pick a name for the new skin. (Here we use "xyzzy").
30 **
31 ** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt,
32 ** and skins/xyzzy/footer.txt into the source tree.
33 **
34 ** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to
35 ** rebuild the makefiles to reference the new CSS, headers, and footers.
36 **
@@ -42,11 +42,11 @@
42 char *zSQL; /* Filled in at run-time with SQL to insert this skin */
43 } aBuiltinSkin[] = {
44 { "Default", "default", 0 },
45 { "Plain Gray, No Logo", "plain_gray", 0 },
46 { "Khaki, No Logo", "khaki", 0 },
47 { "Black & White, Menu on Left", "black_and_white", 0 },
48 { "Shadow boxes & Rounded Corners", "rounded1", 0 },
49 { "Enhanced Default", "enhanced1", 0 },
50 { "San Francisco Modern", "etienne1", 0 },
51 { "Eagle", "eagle", 0 },
52 };
53
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -134,10 +134,11 @@
134134
const void *notUsed
135135
){
136136
add_content_sql_commands(db);
137137
db_add_aux_functions(db);
138138
re_add_sql_func(db);
139
+ search_sql_setup(db);
139140
g.zMainDbType = "repository";
140141
foci_register(db);
141142
g.repositoryOpen = 1;
142143
g.db = db;
143144
return SQLITE_OK;
144145
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -134,10 +134,11 @@
134 const void *notUsed
135 ){
136 add_content_sql_commands(db);
137 db_add_aux_functions(db);
138 re_add_sql_func(db);
 
139 g.zMainDbType = "repository";
140 foci_register(db);
141 g.repositoryOpen = 1;
142 g.db = db;
143 return SQLITE_OK;
144
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -134,10 +134,11 @@
134 const void *notUsed
135 ){
136 add_content_sql_commands(db);
137 db_add_aux_functions(db);
138 re_add_sql_func(db);
139 search_sql_setup(db);
140 g.zMainDbType = "repository";
141 foci_register(db);
142 g.repositoryOpen = 1;
143 g.db = db;
144 return SQLITE_OK;
145
+109 -116
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
11
/******************************************************************************
22
** This file is an amalgamation of many separate C source files from SQLite
3
-** version 3.8.8.1. By combining all the individual C code files into this
3
+** version 3.8.8.2. By combining all the individual C code files into this
44
** single large file, the entire code can be compiled as a single translation
55
** unit. This allows many compilers to do optimizations that would not be
66
** possible if the files were compiled separately. Performance improvements
77
** of 5% or more are commonly seen when SQLite is compiled as a single
88
** translation unit.
@@ -276,13 +276,13 @@
276276
**
277277
** See also: [sqlite3_libversion()],
278278
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
279279
** [sqlite_version()] and [sqlite_source_id()].
280280
*/
281
-#define SQLITE_VERSION "3.8.8.1"
281
+#define SQLITE_VERSION "3.8.8.2"
282282
#define SQLITE_VERSION_NUMBER 3008008
283
-#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55"
283
+#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098"
284284
285285
/*
286286
** CAPI3REF: Run-Time Library Version Numbers
287287
** KEYWORDS: sqlite3_version, sqlite3_sourceid
288288
**
@@ -19870,21 +19870,10 @@
1987019870
# define SQLITE_WIN32_VOLATILE
1987119871
#else
1987219872
# define SQLITE_WIN32_VOLATILE volatile
1987319873
#endif
1987419874
19875
-/*
19876
-** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
19877
-** functions are not available (e.g. those not using MSVC, Cygwin, etc).
19878
-*/
19879
-#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
19880
- SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
19881
-# define SQLITE_OS_WIN_THREADS 1
19882
-#else
19883
-# define SQLITE_OS_WIN_THREADS 0
19884
-#endif
19885
-
1988619875
#endif /* _OS_WIN_H_ */
1988719876
1988819877
/************** End of os_win.h **********************************************/
1988919878
/************** Continuing where we left off in mutex_w32.c ******************/
1989019879
#endif
@@ -22444,11 +22433,11 @@
2244422433
#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
2244522434
/******************************** End Unix Pthreads *************************/
2244622435
2244722436
2244822437
/********************************* Win32 Threads ****************************/
22449
-#if SQLITE_OS_WIN_THREADS
22438
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
2245022439
2245122440
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
2245222441
#include <process.h>
2245322442
2245422443
/* A running thread */
@@ -22537,11 +22526,11 @@
2253722526
if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
2253822527
sqlite3_free(p);
2253922528
return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
2254022529
}
2254122530
22542
-#endif /* SQLITE_OS_WIN_THREADS */
22531
+#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */
2254322532
/******************************** End Win32 Threads *************************/
2254422533
2254522534
2254622535
/********************************* Single-Threaded **************************/
2254722536
#ifndef SQLITE_THREADS_IMPLEMENTED
@@ -50206,11 +50195,11 @@
5020650195
int (*xBusy)(void*), /* Function to call when busy */
5020750196
void *pBusyArg, /* Context argument for xBusyHandler */
5020850197
int sync_flags, /* Flags for OsSync() (or 0) */
5020950198
u8 *zBuf /* Temporary buffer to use */
5021050199
){
50211
- int rc; /* Return code */
50200
+ int rc = SQLITE_OK; /* Return code */
5021250201
int szPage; /* Database page-size */
5021350202
WalIterator *pIter = 0; /* Wal iterator context */
5021450203
u32 iDbpage = 0; /* Next database page to write */
5021550204
u32 iFrame = 0; /* Wal frame containing data for iDbpage */
5021650205
u32 mxSafeFrame; /* Max frame that can be backfilled */
@@ -50220,108 +50209,111 @@
5022050209
5022150210
szPage = walPagesize(pWal);
5022250211
testcase( szPage<=32768 );
5022350212
testcase( szPage>=65536 );
5022450213
pInfo = walCkptInfo(pWal);
50225
- if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
50226
-
50227
- /* Allocate the iterator */
50228
- rc = walIteratorInit(pWal, &pIter);
50229
- if( rc!=SQLITE_OK ){
50230
- return rc;
50231
- }
50232
- assert( pIter );
50233
-
50234
- /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
50235
- ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
50236
- assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
50237
-
50238
- /* Compute in mxSafeFrame the index of the last frame of the WAL that is
50239
- ** safe to write into the database. Frames beyond mxSafeFrame might
50240
- ** overwrite database pages that are in use by active readers and thus
50241
- ** cannot be backfilled from the WAL.
50242
- */
50243
- mxSafeFrame = pWal->hdr.mxFrame;
50244
- mxPage = pWal->hdr.nPage;
50245
- for(i=1; i<WAL_NREADER; i++){
50246
- u32 y = pInfo->aReadMark[i];
50247
- if( mxSafeFrame>y ){
50248
- assert( y<=pWal->hdr.mxFrame );
50249
- rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
50250
- if( rc==SQLITE_OK ){
50251
- pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
50252
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
50253
- }else if( rc==SQLITE_BUSY ){
50254
- mxSafeFrame = y;
50255
- xBusy = 0;
50256
- }else{
50257
- goto walcheckpoint_out;
50258
- }
50259
- }
50260
- }
50261
-
50262
- if( pInfo->nBackfill<mxSafeFrame
50263
- && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
50264
- ){
50265
- i64 nSize; /* Current size of database file */
50266
- u32 nBackfill = pInfo->nBackfill;
50267
-
50268
- /* Sync the WAL to disk */
50269
- if( sync_flags ){
50270
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
50271
- }
50272
-
50273
- /* If the database may grow as a result of this checkpoint, hint
50274
- ** about the eventual size of the db file to the VFS layer.
50275
- */
50276
- if( rc==SQLITE_OK ){
50277
- i64 nReq = ((i64)mxPage * szPage);
50278
- rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
50279
- if( rc==SQLITE_OK && nSize<nReq ){
50280
- sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
50281
- }
50282
- }
50283
-
50284
-
50285
- /* Iterate through the contents of the WAL, copying data to the db file. */
50286
- while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
50287
- i64 iOffset;
50288
- assert( walFramePgno(pWal, iFrame)==iDbpage );
50289
- if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
50290
- iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
50291
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
50292
- rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
50293
- if( rc!=SQLITE_OK ) break;
50294
- iOffset = (iDbpage-1)*(i64)szPage;
50295
- testcase( IS_BIG_INT(iOffset) );
50296
- rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
50297
- if( rc!=SQLITE_OK ) break;
50298
- }
50299
-
50300
- /* If work was actually accomplished... */
50301
- if( rc==SQLITE_OK ){
50302
- if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
50303
- i64 szDb = pWal->hdr.nPage*(i64)szPage;
50304
- testcase( IS_BIG_INT(szDb) );
50305
- rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
50306
- if( rc==SQLITE_OK && sync_flags ){
50307
- rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
50308
- }
50309
- }
50310
- if( rc==SQLITE_OK ){
50311
- pInfo->nBackfill = mxSafeFrame;
50312
- }
50313
- }
50314
-
50315
- /* Release the reader lock held while backfilling */
50316
- walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
50317
- }
50318
-
50319
- if( rc==SQLITE_BUSY ){
50320
- /* Reset the return code so as not to report a checkpoint failure
50321
- ** just because there are active readers. */
50322
- rc = SQLITE_OK;
50214
+ if( pInfo->nBackfill<pWal->hdr.mxFrame ){
50215
+
50216
+ /* Allocate the iterator */
50217
+ rc = walIteratorInit(pWal, &pIter);
50218
+ if( rc!=SQLITE_OK ){
50219
+ return rc;
50220
+ }
50221
+ assert( pIter );
50222
+
50223
+ /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
50224
+ ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
50225
+ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
50226
+
50227
+ /* Compute in mxSafeFrame the index of the last frame of the WAL that is
50228
+ ** safe to write into the database. Frames beyond mxSafeFrame might
50229
+ ** overwrite database pages that are in use by active readers and thus
50230
+ ** cannot be backfilled from the WAL.
50231
+ */
50232
+ mxSafeFrame = pWal->hdr.mxFrame;
50233
+ mxPage = pWal->hdr.nPage;
50234
+ for(i=1; i<WAL_NREADER; i++){
50235
+ u32 y = pInfo->aReadMark[i];
50236
+ if( mxSafeFrame>y ){
50237
+ assert( y<=pWal->hdr.mxFrame );
50238
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
50239
+ if( rc==SQLITE_OK ){
50240
+ pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
50241
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
50242
+ }else if( rc==SQLITE_BUSY ){
50243
+ mxSafeFrame = y;
50244
+ xBusy = 0;
50245
+ }else{
50246
+ goto walcheckpoint_out;
50247
+ }
50248
+ }
50249
+ }
50250
+
50251
+ if( pInfo->nBackfill<mxSafeFrame
50252
+ && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
50253
+ ){
50254
+ i64 nSize; /* Current size of database file */
50255
+ u32 nBackfill = pInfo->nBackfill;
50256
+
50257
+ /* Sync the WAL to disk */
50258
+ if( sync_flags ){
50259
+ rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
50260
+ }
50261
+
50262
+ /* If the database may grow as a result of this checkpoint, hint
50263
+ ** about the eventual size of the db file to the VFS layer.
50264
+ */
50265
+ if( rc==SQLITE_OK ){
50266
+ i64 nReq = ((i64)mxPage * szPage);
50267
+ rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
50268
+ if( rc==SQLITE_OK && nSize<nReq ){
50269
+ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
50270
+ }
50271
+ }
50272
+
50273
+
50274
+ /* Iterate through the contents of the WAL, copying data to the db file */
50275
+ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
50276
+ i64 iOffset;
50277
+ assert( walFramePgno(pWal, iFrame)==iDbpage );
50278
+ if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
50279
+ continue;
50280
+ }
50281
+ iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
50282
+ /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
50283
+ rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
50284
+ if( rc!=SQLITE_OK ) break;
50285
+ iOffset = (iDbpage-1)*(i64)szPage;
50286
+ testcase( IS_BIG_INT(iOffset) );
50287
+ rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
50288
+ if( rc!=SQLITE_OK ) break;
50289
+ }
50290
+
50291
+ /* If work was actually accomplished... */
50292
+ if( rc==SQLITE_OK ){
50293
+ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
50294
+ i64 szDb = pWal->hdr.nPage*(i64)szPage;
50295
+ testcase( IS_BIG_INT(szDb) );
50296
+ rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
50297
+ if( rc==SQLITE_OK && sync_flags ){
50298
+ rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
50299
+ }
50300
+ }
50301
+ if( rc==SQLITE_OK ){
50302
+ pInfo->nBackfill = mxSafeFrame;
50303
+ }
50304
+ }
50305
+
50306
+ /* Release the reader lock held while backfilling */
50307
+ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
50308
+ }
50309
+
50310
+ if( rc==SQLITE_BUSY ){
50311
+ /* Reset the return code so as not to report a checkpoint failure
50312
+ ** just because there are active readers. */
50313
+ rc = SQLITE_OK;
50314
+ }
5032350315
}
5032450316
5032550317
/* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
5032650318
** entire wal file has been copied into the database file, then block
5032750319
** until all readers have finished using the wal file. This ensures that
@@ -50332,11 +50324,11 @@
5033250324
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
5033350325
rc = SQLITE_BUSY;
5033450326
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
5033550327
u32 salt1;
5033650328
sqlite3_randomness(4, &salt1);
50337
- assert( mxSafeFrame==pWal->hdr.mxFrame );
50329
+ assert( pInfo->nBackfill==pWal->hdr.mxFrame );
5033850330
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
5033950331
if( rc==SQLITE_OK ){
5034050332
if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
5034150333
/* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
5034250334
** SQLITE_CHECKPOINT_RESTART with the addition that it also
@@ -128378,10 +128370,11 @@
128378128370
}
128379128371
if( iDb<0 ){
128380128372
rc = SQLITE_ERROR;
128381128373
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
128382128374
}else{
128375
+ db->busyHandler.nBusy = 0;
128383128376
rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
128384128377
sqlite3Error(db, rc);
128385128378
}
128386128379
rc = sqlite3ApiExit(db, rc);
128387128380
sqlite3_mutex_leave(db->mutex);
128388128381
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.8.8.1. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -276,13 +276,13 @@
276 **
277 ** See also: [sqlite3_libversion()],
278 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
279 ** [sqlite_version()] and [sqlite_source_id()].
280 */
281 #define SQLITE_VERSION "3.8.8.1"
282 #define SQLITE_VERSION_NUMBER 3008008
283 #define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55"
284
285 /*
286 ** CAPI3REF: Run-Time Library Version Numbers
287 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
288 **
@@ -19870,21 +19870,10 @@
19870 # define SQLITE_WIN32_VOLATILE
19871 #else
19872 # define SQLITE_WIN32_VOLATILE volatile
19873 #endif
19874
19875 /*
19876 ** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
19877 ** functions are not available (e.g. those not using MSVC, Cygwin, etc).
19878 */
19879 #if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
19880 SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
19881 # define SQLITE_OS_WIN_THREADS 1
19882 #else
19883 # define SQLITE_OS_WIN_THREADS 0
19884 #endif
19885
19886 #endif /* _OS_WIN_H_ */
19887
19888 /************** End of os_win.h **********************************************/
19889 /************** Continuing where we left off in mutex_w32.c ******************/
19890 #endif
@@ -22444,11 +22433,11 @@
22444 #endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
22445 /******************************** End Unix Pthreads *************************/
22446
22447
22448 /********************************* Win32 Threads ****************************/
22449 #if SQLITE_OS_WIN_THREADS
22450
22451 #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
22452 #include <process.h>
22453
22454 /* A running thread */
@@ -22537,11 +22526,11 @@
22537 if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
22538 sqlite3_free(p);
22539 return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
22540 }
22541
22542 #endif /* SQLITE_OS_WIN_THREADS */
22543 /******************************** End Win32 Threads *************************/
22544
22545
22546 /********************************* Single-Threaded **************************/
22547 #ifndef SQLITE_THREADS_IMPLEMENTED
@@ -50206,11 +50195,11 @@
50206 int (*xBusy)(void*), /* Function to call when busy */
50207 void *pBusyArg, /* Context argument for xBusyHandler */
50208 int sync_flags, /* Flags for OsSync() (or 0) */
50209 u8 *zBuf /* Temporary buffer to use */
50210 ){
50211 int rc; /* Return code */
50212 int szPage; /* Database page-size */
50213 WalIterator *pIter = 0; /* Wal iterator context */
50214 u32 iDbpage = 0; /* Next database page to write */
50215 u32 iFrame = 0; /* Wal frame containing data for iDbpage */
50216 u32 mxSafeFrame; /* Max frame that can be backfilled */
@@ -50220,108 +50209,111 @@
50220
50221 szPage = walPagesize(pWal);
50222 testcase( szPage<=32768 );
50223 testcase( szPage>=65536 );
50224 pInfo = walCkptInfo(pWal);
50225 if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
50226
50227 /* Allocate the iterator */
50228 rc = walIteratorInit(pWal, &pIter);
50229 if( rc!=SQLITE_OK ){
50230 return rc;
50231 }
50232 assert( pIter );
50233
50234 /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
50235 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
50236 assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
50237
50238 /* Compute in mxSafeFrame the index of the last frame of the WAL that is
50239 ** safe to write into the database. Frames beyond mxSafeFrame might
50240 ** overwrite database pages that are in use by active readers and thus
50241 ** cannot be backfilled from the WAL.
50242 */
50243 mxSafeFrame = pWal->hdr.mxFrame;
50244 mxPage = pWal->hdr.nPage;
50245 for(i=1; i<WAL_NREADER; i++){
50246 u32 y = pInfo->aReadMark[i];
50247 if( mxSafeFrame>y ){
50248 assert( y<=pWal->hdr.mxFrame );
50249 rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
50250 if( rc==SQLITE_OK ){
50251 pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
50252 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
50253 }else if( rc==SQLITE_BUSY ){
50254 mxSafeFrame = y;
50255 xBusy = 0;
50256 }else{
50257 goto walcheckpoint_out;
50258 }
50259 }
50260 }
50261
50262 if( pInfo->nBackfill<mxSafeFrame
50263 && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
50264 ){
50265 i64 nSize; /* Current size of database file */
50266 u32 nBackfill = pInfo->nBackfill;
50267
50268 /* Sync the WAL to disk */
50269 if( sync_flags ){
50270 rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
50271 }
50272
50273 /* If the database may grow as a result of this checkpoint, hint
50274 ** about the eventual size of the db file to the VFS layer.
50275 */
50276 if( rc==SQLITE_OK ){
50277 i64 nReq = ((i64)mxPage * szPage);
50278 rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
50279 if( rc==SQLITE_OK && nSize<nReq ){
50280 sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
50281 }
50282 }
50283
50284
50285 /* Iterate through the contents of the WAL, copying data to the db file. */
50286 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
50287 i64 iOffset;
50288 assert( walFramePgno(pWal, iFrame)==iDbpage );
50289 if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
50290 iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
50291 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
50292 rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
50293 if( rc!=SQLITE_OK ) break;
50294 iOffset = (iDbpage-1)*(i64)szPage;
50295 testcase( IS_BIG_INT(iOffset) );
50296 rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
50297 if( rc!=SQLITE_OK ) break;
50298 }
50299
50300 /* If work was actually accomplished... */
50301 if( rc==SQLITE_OK ){
50302 if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
50303 i64 szDb = pWal->hdr.nPage*(i64)szPage;
50304 testcase( IS_BIG_INT(szDb) );
50305 rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
50306 if( rc==SQLITE_OK && sync_flags ){
50307 rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
50308 }
50309 }
50310 if( rc==SQLITE_OK ){
50311 pInfo->nBackfill = mxSafeFrame;
50312 }
50313 }
50314
50315 /* Release the reader lock held while backfilling */
50316 walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
50317 }
50318
50319 if( rc==SQLITE_BUSY ){
50320 /* Reset the return code so as not to report a checkpoint failure
50321 ** just because there are active readers. */
50322 rc = SQLITE_OK;
 
 
 
50323 }
50324
50325 /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
50326 ** entire wal file has been copied into the database file, then block
50327 ** until all readers have finished using the wal file. This ensures that
@@ -50332,11 +50324,11 @@
50332 if( pInfo->nBackfill<pWal->hdr.mxFrame ){
50333 rc = SQLITE_BUSY;
50334 }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
50335 u32 salt1;
50336 sqlite3_randomness(4, &salt1);
50337 assert( mxSafeFrame==pWal->hdr.mxFrame );
50338 rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
50339 if( rc==SQLITE_OK ){
50340 if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
50341 /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
50342 ** SQLITE_CHECKPOINT_RESTART with the addition that it also
@@ -128378,10 +128370,11 @@
128378 }
128379 if( iDb<0 ){
128380 rc = SQLITE_ERROR;
128381 sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
128382 }else{
 
128383 rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
128384 sqlite3Error(db, rc);
128385 }
128386 rc = sqlite3ApiExit(db, rc);
128387 sqlite3_mutex_leave(db->mutex);
128388
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.8.8.2. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -276,13 +276,13 @@
276 **
277 ** See also: [sqlite3_libversion()],
278 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
279 ** [sqlite_version()] and [sqlite_source_id()].
280 */
281 #define SQLITE_VERSION "3.8.8.2"
282 #define SQLITE_VERSION_NUMBER 3008008
283 #define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098"
284
285 /*
286 ** CAPI3REF: Run-Time Library Version Numbers
287 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
288 **
@@ -19870,21 +19870,10 @@
19870 # define SQLITE_WIN32_VOLATILE
19871 #else
19872 # define SQLITE_WIN32_VOLATILE volatile
19873 #endif
19874
 
 
 
 
 
 
 
 
 
 
 
19875 #endif /* _OS_WIN_H_ */
19876
19877 /************** End of os_win.h **********************************************/
19878 /************** Continuing where we left off in mutex_w32.c ******************/
19879 #endif
@@ -22444,11 +22433,11 @@
22433 #endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
22434 /******************************** End Unix Pthreads *************************/
22435
22436
22437 /********************************* Win32 Threads ****************************/
22438 #if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
22439
22440 #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
22441 #include <process.h>
22442
22443 /* A running thread */
@@ -22537,11 +22526,11 @@
22526 if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
22527 sqlite3_free(p);
22528 return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
22529 }
22530
22531 #endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */
22532 /******************************** End Win32 Threads *************************/
22533
22534
22535 /********************************* Single-Threaded **************************/
22536 #ifndef SQLITE_THREADS_IMPLEMENTED
@@ -50206,11 +50195,11 @@
50195 int (*xBusy)(void*), /* Function to call when busy */
50196 void *pBusyArg, /* Context argument for xBusyHandler */
50197 int sync_flags, /* Flags for OsSync() (or 0) */
50198 u8 *zBuf /* Temporary buffer to use */
50199 ){
50200 int rc = SQLITE_OK; /* Return code */
50201 int szPage; /* Database page-size */
50202 WalIterator *pIter = 0; /* Wal iterator context */
50203 u32 iDbpage = 0; /* Next database page to write */
50204 u32 iFrame = 0; /* Wal frame containing data for iDbpage */
50205 u32 mxSafeFrame; /* Max frame that can be backfilled */
@@ -50220,108 +50209,111 @@
50209
50210 szPage = walPagesize(pWal);
50211 testcase( szPage<=32768 );
50212 testcase( szPage>=65536 );
50213 pInfo = walCkptInfo(pWal);
50214 if( pInfo->nBackfill<pWal->hdr.mxFrame ){
50215
50216 /* Allocate the iterator */
50217 rc = walIteratorInit(pWal, &pIter);
50218 if( rc!=SQLITE_OK ){
50219 return rc;
50220 }
50221 assert( pIter );
50222
50223 /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
50224 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
50225 assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
50226
50227 /* Compute in mxSafeFrame the index of the last frame of the WAL that is
50228 ** safe to write into the database. Frames beyond mxSafeFrame might
50229 ** overwrite database pages that are in use by active readers and thus
50230 ** cannot be backfilled from the WAL.
50231 */
50232 mxSafeFrame = pWal->hdr.mxFrame;
50233 mxPage = pWal->hdr.nPage;
50234 for(i=1; i<WAL_NREADER; i++){
50235 u32 y = pInfo->aReadMark[i];
50236 if( mxSafeFrame>y ){
50237 assert( y<=pWal->hdr.mxFrame );
50238 rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
50239 if( rc==SQLITE_OK ){
50240 pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
50241 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
50242 }else if( rc==SQLITE_BUSY ){
50243 mxSafeFrame = y;
50244 xBusy = 0;
50245 }else{
50246 goto walcheckpoint_out;
50247 }
50248 }
50249 }
50250
50251 if( pInfo->nBackfill<mxSafeFrame
50252 && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
50253 ){
50254 i64 nSize; /* Current size of database file */
50255 u32 nBackfill = pInfo->nBackfill;
50256
50257 /* Sync the WAL to disk */
50258 if( sync_flags ){
50259 rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
50260 }
50261
50262 /* If the database may grow as a result of this checkpoint, hint
50263 ** about the eventual size of the db file to the VFS layer.
50264 */
50265 if( rc==SQLITE_OK ){
50266 i64 nReq = ((i64)mxPage * szPage);
50267 rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
50268 if( rc==SQLITE_OK && nSize<nReq ){
50269 sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
50270 }
50271 }
50272
50273
50274 /* Iterate through the contents of the WAL, copying data to the db file */
50275 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
50276 i64 iOffset;
50277 assert( walFramePgno(pWal, iFrame)==iDbpage );
50278 if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
50279 continue;
50280 }
50281 iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
50282 /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
50283 rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
50284 if( rc!=SQLITE_OK ) break;
50285 iOffset = (iDbpage-1)*(i64)szPage;
50286 testcase( IS_BIG_INT(iOffset) );
50287 rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
50288 if( rc!=SQLITE_OK ) break;
50289 }
50290
50291 /* If work was actually accomplished... */
50292 if( rc==SQLITE_OK ){
50293 if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
50294 i64 szDb = pWal->hdr.nPage*(i64)szPage;
50295 testcase( IS_BIG_INT(szDb) );
50296 rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
50297 if( rc==SQLITE_OK && sync_flags ){
50298 rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
50299 }
50300 }
50301 if( rc==SQLITE_OK ){
50302 pInfo->nBackfill = mxSafeFrame;
50303 }
50304 }
50305
50306 /* Release the reader lock held while backfilling */
50307 walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
50308 }
50309
50310 if( rc==SQLITE_BUSY ){
50311 /* Reset the return code so as not to report a checkpoint failure
50312 ** just because there are active readers. */
50313 rc = SQLITE_OK;
50314 }
50315 }
50316
50317 /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
50318 ** entire wal file has been copied into the database file, then block
50319 ** until all readers have finished using the wal file. This ensures that
@@ -50332,11 +50324,11 @@
50324 if( pInfo->nBackfill<pWal->hdr.mxFrame ){
50325 rc = SQLITE_BUSY;
50326 }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
50327 u32 salt1;
50328 sqlite3_randomness(4, &salt1);
50329 assert( pInfo->nBackfill==pWal->hdr.mxFrame );
50330 rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
50331 if( rc==SQLITE_OK ){
50332 if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
50333 /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
50334 ** SQLITE_CHECKPOINT_RESTART with the addition that it also
@@ -128378,10 +128370,11 @@
128370 }
128371 if( iDb<0 ){
128372 rc = SQLITE_ERROR;
128373 sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
128374 }else{
128375 db->busyHandler.nBusy = 0;
128376 rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
128377 sqlite3Error(db, rc);
128378 }
128379 rc = sqlite3ApiExit(db, rc);
128380 sqlite3_mutex_leave(db->mutex);
128381
+2 -2
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105105
**
106106
** See also: [sqlite3_libversion()],
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110
-#define SQLITE_VERSION "3.8.8.1"
110
+#define SQLITE_VERSION "3.8.8.2"
111111
#define SQLITE_VERSION_NUMBER 3008008
112
-#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55"
112
+#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
118118
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.8.8.1"
111 #define SQLITE_VERSION_NUMBER 3008008
112 #define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
118
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.8.8.2"
111 #define SQLITE_VERSION_NUMBER 3008008
112 #define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
118
+39 -2
--- src/style.c
+++ src/style.c
@@ -1125,10 +1125,23 @@
11251125
},
11261126
{ "th.sort.desc:after",
11271127
"Descending sort column marker",
11281128
@ content: '\2191';
11291129
},
1130
+ { "span.snippet>mark",
1131
+ "Search markup",
1132
+ @ background-color: inherit;
1133
+ @ font-weight: bold;
1134
+ },
1135
+ { "div.searchForm",
1136
+ "Container for the search terms entry box",
1137
+ @ text-align: center;
1138
+ },
1139
+ { "p.searchEmpty",
1140
+ "Message explaining that there are no search results",
1141
+ @ font-style: italic;
1142
+ },
11301143
{ 0,
11311144
0,
11321145
0
11331146
}
11341147
};
@@ -1149,23 +1162,47 @@
11491162
);
11501163
}
11511164
}
11521165
}
11531166
1167
+/*
1168
+** Search string zHaystack for zNeedle. zNeedle must be an isolated
1169
+** word with space or punctuation on either size.
1170
+**
1171
+** Return true if found. Return false if not found
1172
+*/
1173
+static int containsString(const char *zHaystack, const char *zNeedle){
1174
+ char *z;
1175
+ int n;
1176
+
1177
+ while( zHaystack[0] ){
1178
+ z = strstr(zHaystack, zNeedle);
1179
+ if( z==0 ) return 0;
1180
+ n = (int)strlen(zNeedle);
1181
+ if( (z==zHaystack || !fossil_isalnum(z[-1])) && !fossil_isalnum(z[n]) ){
1182
+ return 1;
1183
+ }
1184
+ zHaystack = z + n;
1185
+ }
1186
+ return 0;
1187
+}
1188
+
1189
+
11541190
/*
11551191
** WEBPAGE: style.css
11561192
*/
11571193
void page_style_css(void){
11581194
Blob css;
11591195
int i;
11601196
11611197
cgi_set_content_type("text/css");
1162
- blob_init(&css, db_get("css",(char*)builtin_text("skins/default/css.txt")), -1);
1198
+ blob_init(&css,db_get("css",(char*)builtin_text("skins/default/css.txt")),-1);
11631199
11641200
/* add special missing definitions */
11651201
for(i=1; cssDefaultList[i].elementClass; i++){
1166
- if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){
1202
+ char *z = blob_str(&css);
1203
+ if( !containsString(z, cssDefaultList[i].elementClass) ){
11671204
blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
11681205
cssDefaultList[i].comment,
11691206
cssDefaultList[i].elementClass,
11701207
cssDefaultList[i].value);
11711208
}
11721209
--- src/style.c
+++ src/style.c
@@ -1125,10 +1125,23 @@
1125 },
1126 { "th.sort.desc:after",
1127 "Descending sort column marker",
1128 @ content: '\2191';
1129 },
 
 
 
 
 
 
 
 
 
 
 
 
 
1130 { 0,
1131 0,
1132 0
1133 }
1134 };
@@ -1149,23 +1162,47 @@
1149 );
1150 }
1151 }
1152 }
1153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1154 /*
1155 ** WEBPAGE: style.css
1156 */
1157 void page_style_css(void){
1158 Blob css;
1159 int i;
1160
1161 cgi_set_content_type("text/css");
1162 blob_init(&css, db_get("css",(char*)builtin_text("skins/default/css.txt")), -1);
1163
1164 /* add special missing definitions */
1165 for(i=1; cssDefaultList[i].elementClass; i++){
1166 if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){
 
1167 blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
1168 cssDefaultList[i].comment,
1169 cssDefaultList[i].elementClass,
1170 cssDefaultList[i].value);
1171 }
1172
--- src/style.c
+++ src/style.c
@@ -1125,10 +1125,23 @@
1125 },
1126 { "th.sort.desc:after",
1127 "Descending sort column marker",
1128 @ content: '\2191';
1129 },
1130 { "span.snippet>mark",
1131 "Search markup",
1132 @ background-color: inherit;
1133 @ font-weight: bold;
1134 },
1135 { "div.searchForm",
1136 "Container for the search terms entry box",
1137 @ text-align: center;
1138 },
1139 { "p.searchEmpty",
1140 "Message explaining that there are no search results",
1141 @ font-style: italic;
1142 },
1143 { 0,
1144 0,
1145 0
1146 }
1147 };
@@ -1149,23 +1162,47 @@
1162 );
1163 }
1164 }
1165 }
1166
1167 /*
1168 ** Search string zHaystack for zNeedle. zNeedle must be an isolated
1169 ** word with space or punctuation on either size.
1170 **
1171 ** Return true if found. Return false if not found
1172 */
1173 static int containsString(const char *zHaystack, const char *zNeedle){
1174 char *z;
1175 int n;
1176
1177 while( zHaystack[0] ){
1178 z = strstr(zHaystack, zNeedle);
1179 if( z==0 ) return 0;
1180 n = (int)strlen(zNeedle);
1181 if( (z==zHaystack || !fossil_isalnum(z[-1])) && !fossil_isalnum(z[n]) ){
1182 return 1;
1183 }
1184 zHaystack = z + n;
1185 }
1186 return 0;
1187 }
1188
1189
1190 /*
1191 ** WEBPAGE: style.css
1192 */
1193 void page_style_css(void){
1194 Blob css;
1195 int i;
1196
1197 cgi_set_content_type("text/css");
1198 blob_init(&css,db_get("css",(char*)builtin_text("skins/default/css.txt")),-1);
1199
1200 /* add special missing definitions */
1201 for(i=1; cssDefaultList[i].elementClass; i++){
1202 char *z = blob_str(&css);
1203 if( !containsString(z, cssDefaultList[i].elementClass) ){
1204 blob_appendf(&css, "/* %s */\n%s {\n%s}\n",
1205 cssDefaultList[i].comment,
1206 cssDefaultList[i].elementClass,
1207 cssDefaultList[i].value);
1208 }
1209
+57
--- src/tkt.c
+++ src/tkt.c
@@ -313,10 +313,11 @@
313313
314314
fossil_free(zTag);
315315
getAllTicketFields();
316316
if( haveTicket==0 ) return;
317317
tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid);
318
+ search_doc_touch('t', tktid, 0);
318319
if( haveTicketChng ){
319320
db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid);
320321
}
321322
db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid);
322323
tktid = 0;
@@ -691,10 +692,11 @@
691692
if( !g.perm.NewTkt ){ login_needed(); return; }
692693
if( P("cancel") ){
693694
cgi_redirect("home");
694695
}
695696
style_header("New Ticket");
697
+ ticket_standard_submenu(T_ALL_BUT(T_NEW));
696698
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
697699
ticket_init();
698700
initializeVariablesFromCGI();
699701
getAllTicketFields();
700702
initializeVariablesFromDb();
@@ -1380,5 +1382,60 @@
13801382
(eCmd==set?"set":"add"),zTktUuid);
13811383
}
13821384
}
13831385
}
13841386
}
1387
+
1388
+
1389
+#if INTERFACE
1390
+/* Standard submenu items for wiki pages */
1391
+#define T_SRCH 0x00001
1392
+#define T_REPLIST 0x00002
1393
+#define T_NEW 0x00004
1394
+#define T_ALL 0x00007
1395
+#define T_ALL_BUT(x) (T_ALL&~(x))
1396
+#endif
1397
+
1398
+/*
1399
+** Add some standard submenu elements for ticket screens.
1400
+*/
1401
+void ticket_standard_submenu(unsigned int ok){
1402
+ if( (ok & T_SRCH)!=0 && search_restrict(SRCH_TKT)!=0 ){
1403
+ style_submenu_element("Search","Search","%R/tktsrch");
1404
+ }
1405
+ if( (ok & T_REPLIST)!=0 ){
1406
+ style_submenu_element("Reports","Reports","%R/reportlist");
1407
+ }
1408
+ if( (ok & T_NEW)!=0 && g.perm.NewTkt ){
1409
+ style_submenu_element("New","New","%R/tktnew");
1410
+ }
1411
+}
1412
+
1413
+/*
1414
+** WEBPAGE: ticket
1415
+**
1416
+** This is intended to be the primary "Ticket" page. Render as
1417
+** either ticket-search (if search is enabled) or as the
1418
+** /reportlist page (if ticket search is disabled).
1419
+*/
1420
+void tkt_home_page(void){
1421
+ login_check_credentials();
1422
+ if( search_restrict(SRCH_TKT)!=0 ){
1423
+ tkt_srchpage();
1424
+ }else{
1425
+ view_list();
1426
+ }
1427
+}
1428
+
1429
+/*
1430
+** WEBPAGE: tktsrch
1431
+** Usage: /tktsrch?s=PATTERN
1432
+**
1433
+** Full-text search of all current tickets
1434
+*/
1435
+void tkt_srchpage(void){
1436
+ login_check_credentials();
1437
+ style_header("Ticket Search");
1438
+ ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1439
+ search_screen(SRCH_TKT, "tktsrch");
1440
+ style_footer();
1441
+}
13851442
--- src/tkt.c
+++ src/tkt.c
@@ -313,10 +313,11 @@
313
314 fossil_free(zTag);
315 getAllTicketFields();
316 if( haveTicket==0 ) return;
317 tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid);
 
318 if( haveTicketChng ){
319 db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid);
320 }
321 db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid);
322 tktid = 0;
@@ -691,10 +692,11 @@
691 if( !g.perm.NewTkt ){ login_needed(); return; }
692 if( P("cancel") ){
693 cgi_redirect("home");
694 }
695 style_header("New Ticket");
 
696 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
697 ticket_init();
698 initializeVariablesFromCGI();
699 getAllTicketFields();
700 initializeVariablesFromDb();
@@ -1380,5 +1382,60 @@
1380 (eCmd==set?"set":"add"),zTktUuid);
1381 }
1382 }
1383 }
1384 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1385
--- src/tkt.c
+++ src/tkt.c
@@ -313,10 +313,11 @@
313
314 fossil_free(zTag);
315 getAllTicketFields();
316 if( haveTicket==0 ) return;
317 tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid);
318 search_doc_touch('t', tktid, 0);
319 if( haveTicketChng ){
320 db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid);
321 }
322 db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid);
323 tktid = 0;
@@ -691,10 +692,11 @@
692 if( !g.perm.NewTkt ){ login_needed(); return; }
693 if( P("cancel") ){
694 cgi_redirect("home");
695 }
696 style_header("New Ticket");
697 ticket_standard_submenu(T_ALL_BUT(T_NEW));
698 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
699 ticket_init();
700 initializeVariablesFromCGI();
701 getAllTicketFields();
702 initializeVariablesFromDb();
@@ -1380,5 +1382,60 @@
1382 (eCmd==set?"set":"add"),zTktUuid);
1383 }
1384 }
1385 }
1386 }
1387
1388
1389 #if INTERFACE
1390 /* Standard submenu items for wiki pages */
1391 #define T_SRCH 0x00001
1392 #define T_REPLIST 0x00002
1393 #define T_NEW 0x00004
1394 #define T_ALL 0x00007
1395 #define T_ALL_BUT(x) (T_ALL&~(x))
1396 #endif
1397
1398 /*
1399 ** Add some standard submenu elements for ticket screens.
1400 */
1401 void ticket_standard_submenu(unsigned int ok){
1402 if( (ok & T_SRCH)!=0 && search_restrict(SRCH_TKT)!=0 ){
1403 style_submenu_element("Search","Search","%R/tktsrch");
1404 }
1405 if( (ok & T_REPLIST)!=0 ){
1406 style_submenu_element("Reports","Reports","%R/reportlist");
1407 }
1408 if( (ok & T_NEW)!=0 && g.perm.NewTkt ){
1409 style_submenu_element("New","New","%R/tktnew");
1410 }
1411 }
1412
1413 /*
1414 ** WEBPAGE: ticket
1415 **
1416 ** This is intended to be the primary "Ticket" page. Render as
1417 ** either ticket-search (if search is enabled) or as the
1418 ** /reportlist page (if ticket search is disabled).
1419 */
1420 void tkt_home_page(void){
1421 login_check_credentials();
1422 if( search_restrict(SRCH_TKT)!=0 ){
1423 tkt_srchpage();
1424 }else{
1425 view_list();
1426 }
1427 }
1428
1429 /*
1430 ** WEBPAGE: tktsrch
1431 ** Usage: /tktsrch?s=PATTERN
1432 **
1433 ** Full-text search of all current tickets
1434 */
1435 void tkt_srchpage(void){
1436 login_check_credentials();
1437 style_header("Ticket Search");
1438 ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1439 search_screen(SRCH_TKT, "tktsrch");
1440 style_footer();
1441 }
1442
+1 -1
--- src/url.c
+++ src/url.c
@@ -389,11 +389,11 @@
389389
void url_enable_proxy(const char *zMsg){
390390
const char *zProxy;
391391
zProxy = zProxyOpt;
392392
if( zProxy==0 ){
393393
zProxy = db_get("proxy", 0);
394
- if( zProxy==0 || zProxy[0]==0 || is_truth(zProxy) ){
394
+ if( zProxy==0 || zProxy[0]==0 || is_false(zProxy) ){
395395
zProxy = fossil_getenv("http_proxy");
396396
}
397397
}
398398
if( zProxy && zProxy[0] && !is_false(zProxy)
399399
&& !g.url.isSsh && !g.url.isFile ){
400400
--- src/url.c
+++ src/url.c
@@ -389,11 +389,11 @@
389 void url_enable_proxy(const char *zMsg){
390 const char *zProxy;
391 zProxy = zProxyOpt;
392 if( zProxy==0 ){
393 zProxy = db_get("proxy", 0);
394 if( zProxy==0 || zProxy[0]==0 || is_truth(zProxy) ){
395 zProxy = fossil_getenv("http_proxy");
396 }
397 }
398 if( zProxy && zProxy[0] && !is_false(zProxy)
399 && !g.url.isSsh && !g.url.isFile ){
400
--- src/url.c
+++ src/url.c
@@ -389,11 +389,11 @@
389 void url_enable_proxy(const char *zMsg){
390 const char *zProxy;
391 zProxy = zProxyOpt;
392 if( zProxy==0 ){
393 zProxy = db_get("proxy", 0);
394 if( zProxy==0 || zProxy[0]==0 || is_false(zProxy) ){
395 zProxy = fossil_getenv("http_proxy");
396 }
397 }
398 if( zProxy && zProxy[0] && !is_false(zProxy)
399 && !g.url.isSsh && !g.url.isFile ){
400
+124 -48
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,106 @@
198198
if( localUser ){
199199
return 0;
200200
}
201201
return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202202
}
203
+
204
+/* Standard submenu items for wiki pages */
205
+#define W_SRCH 0x00001
206
+#define W_LIST 0x00002
207
+#define W_HELP 0x00004
208
+#define W_NEW 0x00008
209
+#define W_BLOG 0x00010
210
+#define W_SANDBOX 0x00020
211
+#define W_ALL 0x0001f
212
+#define W_ALL_BUT(x) (W_ALL&~(x))
213
+
214
+/*
215
+** Add some standard submenu elements for wiki screens.
216
+*/
217
+static void wiki_standard_submenu(unsigned int ok){
218
+ if( (ok & W_SRCH)!=0 && search_restrict(SRCH_WIKI)!=0 ){
219
+ style_submenu_element("Search","Search","%R/wikisrch");
220
+ }
221
+ if( (ok & W_LIST)!=0 ){
222
+ style_submenu_element("List","List","%R/wcontent");
223
+ }
224
+ if( (ok & W_HELP)!=0 ){
225
+ style_submenu_element("Help","Help","%R/wikihelp");
226
+ }
227
+ if( (ok & W_NEW)!=0 && g.perm.NewWiki ){
228
+ style_submenu_element("New","New","%R/wikinew");
229
+ }
230
+#if 0
231
+ if( (ok & W_BLOG)!=0
232
+#endif
233
+ if( (ok & W_SANDBOX)!=0 ){
234
+ style_submenu_element("Sandbox", "Sandbox", "%R/wiki?name=Sandbox");
235
+ }
236
+}
237
+
238
+/*
239
+** WEBPAGE: wikihelp
240
+** A generic landing page for wiki.
241
+*/
242
+void wiki_helppage(void){
243
+ login_check_credentials();
244
+ if( !g.perm.RdWiki ){ login_needed(); return; }
245
+ style_header("Wiki Help");
246
+ wiki_standard_submenu(W_ALL_BUT(W_HELP));
247
+ @ <h2>Wiki Links</h2>
248
+ @ <ul>
249
+ { char *zWikiHomePageName = db_get("index-page",0);
250
+ if( zWikiHomePageName ){
251
+ @ <li> %z(href("%R%s",zWikiHomePageName))
252
+ @ %h(zWikiHomePageName)</a> wiki home page.</li>
253
+ }
254
+ }
255
+ { char *zHomePageName = db_get("project-name",0);
256
+ if( zHomePageName ){
257
+ @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
258
+ @ %h(zHomePageName)</a> project home page.</li>
259
+ }
260
+ }
261
+ @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
262
+ @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
263
+ @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
264
+ @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
265
+ @ to experiment.</li>
266
+ if( g.perm.NewWiki ){
267
+ @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
268
+ if( g.perm.Write ){
269
+ @ <li> Create a %z(href("%R/eventedit"))new blog entry</a>.</li>
270
+ }
271
+ }
272
+ @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
273
+ @ available on this server.</li>
274
+ if( g.perm.ModWiki ){
275
+ @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
276
+ }
277
+ if( search_restrict(SRCH_WIKI)!=0 ){
278
+ @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
279
+ @ words</li>
280
+ }
281
+ @ </ul>
282
+ style_footer();
283
+ return;
284
+}
285
+
286
+/*
287
+** WEBPAGE: wikisrch
288
+** Usage: /wikisrch?s=PATTERN
289
+**
290
+** Full-text search of all current wiki text
291
+*/
292
+void wiki_srchpage(void){
293
+ login_check_credentials();
294
+ style_header("Wiki Search");
295
+ wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
296
+ search_screen(SRCH_WIKI, "wikisrch");
297
+ style_footer();
298
+}
203299
204300
/*
205301
** WEBPAGE: wiki
206302
** URL: /wiki?name=PAGENAME
207303
*/
@@ -208,10 +304,11 @@
208304
void wiki_page(void){
209305
char *zTag;
210306
int rid = 0;
211307
int isSandbox;
212308
char *zUuid;
309
+ unsigned submenuFlags = W_ALL;
213310
Blob wiki;
214311
Manifest *pWiki = 0;
215312
const char *zPageName;
216313
const char *zMimetype = 0;
217314
char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,63 +315,35 @@
218315
219316
login_check_credentials();
220317
if( !g.perm.RdWiki ){ login_needed(); return; }
221318
zPageName = P("name");
222319
if( zPageName==0 ){
223
- style_header("Wiki");
224
- @ <ul>
225
- { char *zWikiHomePageName = db_get("index-page",0);
226
- if( zWikiHomePageName ){
227
- @ <li> %z(href("%R%s",zWikiHomePageName))
228
- @ %h(zWikiHomePageName)</a> wiki home page.</li>
229
- }
230
- }
231
- { char *zHomePageName = db_get("project-name",0);
232
- if( zHomePageName ){
233
- @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
234
- @ %h(zHomePageName)</a> project home page.</li>
235
- }
236
- }
237
- @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
238
- @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
239
- @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
240
- @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
241
- @ to experiment.</li>
242
- if( g.perm.NewWiki ){
243
- @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
244
- if( g.perm.Write ){
245
- @ <li> Create a %z(href("%R/eventedit"))new event</a>.</li>
246
- }
247
- }
248
- @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
249
- @ available on this server.</li>
250
- if( g.perm.ModWiki ){
251
- @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
252
- }
253
- @ <li>
254
- form_begin(0, "%R/wfind");
255
- @ <div>Search wiki titles: <input type="text" name="title"/>
256
- @ &nbsp; <input type="submit" /></div></form>
257
- @ </li>
258
- @ </ul>
259
- style_footer();
320
+ if( search_restrict(SRCH_WIKI)!=0 ){
321
+ wiki_srchpage();
322
+ }else{
323
+ wiki_helppage();
324
+ }
260325
return;
261326
}
262327
if( check_name(zPageName) ) return;
263328
isSandbox = is_sandbox(zPageName);
264329
if( isSandbox ){
330
+ submenuFlags &= ~W_SANDBOX;
265331
zBody = db_get("sandbox",zBody);
266332
zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267333
rid = 0;
268334
}else{
269
- zTag = mprintf("wiki-%s", zPageName);
270
- rid = db_int(0,
271
- "SELECT rid FROM tagxref"
272
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
273
- " ORDER BY mtime DESC", zTag
274
- );
275
- free(zTag);
335
+ const char *zUuid = P("id");
336
+ if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){
337
+ zTag = mprintf("wiki-%s", zPageName);
338
+ rid = db_int(0,
339
+ "SELECT rid FROM tagxref"
340
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
341
+ " ORDER BY mtime DESC", zTag
342
+ );
343
+ free(zTag);
344
+ }
276345
pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
277346
if( pWiki ){
278347
zBody = pWiki->zWiki;
279348
zMimetype = pWiki->zMimetype;
280349
}
@@ -314,10 +383,11 @@
314383
g.zTop, zPageName);
315384
}
316385
}
317386
style_set_current_page("%T?name=%T", g.zPath, zPageName);
318387
style_header("%s", zPageName);
388
+ wiki_standard_submenu(submenuFlags);
319389
blob_init(&wiki, zBody, -1);
320390
wiki_render_by_mimetype(&wiki, zMimetype);
321391
blob_reset(&wiki);
322392
attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
323393
manifest_destroy(pWiki);
@@ -570,10 +640,11 @@
570640
}else{
571641
cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
572642
}
573643
}
574644
style_header("Create A New Wiki Page");
645
+ wiki_standard_submenu(W_ALL_BUT(W_NEW));
575646
@ <p>Rules for wiki page names:</p>
576647
well_formed_wiki_name_rules();
577648
form_begin(0, "%R/wikinew");
578649
@ <p>Name of new wiki page:
579650
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -846,11 +917,12 @@
846917
*/
847918
void wiki_prepare_page_list( Stmt * pStmt ){
848919
db_prepare(pStmt,
849920
"SELECT"
850921
" substr(tagname, 6) as name,"
851
- " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
922
+ " (SELECT value FROM tagxref WHERE tagid=tag.tagid"
923
+ " ORDER BY mtime DESC) as tagXref"
852924
" FROM tag WHERE tagname GLOB 'wiki-*'"
853925
" ORDER BY lower(tagname) /*sort*/"
854926
);
855927
}
856928
/*
@@ -870,10 +942,11 @@
870942
if( showAll ){
871943
style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
872944
}else{
873945
style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
874946
}
947
+ wiki_standard_submenu(W_ALL_BUT(W_LIST));
875948
@ <ul>
876949
wiki_prepare_page_list(&q);
877950
while( db_step(&q)==SQLITE_ROW ){
878951
const char *zName = db_column_text(&q, 0);
879952
int size = db_column_int(&q, 1);
@@ -935,11 +1008,12 @@
9351008
@ </ol>
9361009
@ <p>We call the first five rules above "wiki" formatting rules. The
9371010
@ last two rules are the HTML formatting rule.</p>
9381011
@ <h2>Formatting Rule Details</h2>
9391012
@ <ol>
940
- @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
1013
+ @ <li> <p><span class="wikiruleHead">Paragraphs</span>.
1014
+ @ Any sequence of one or more blank lines forms
9411015
@ a paragraph break. Centered or right-justified paragraphs are not
9421016
@ supported by wiki markup, but you can do these things if you need them
9431017
@ using HTML.</p></li>
9441018
@ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
9451019
@ A bullet list item is a line that begins with a single "*" character
@@ -960,11 +1034,12 @@
9601034
@ Text within square brackets ("[...]") becomes a hyperlink. The
9611035
@ target can be a wiki page name, the artifact ID of a check-in or ticket,
9621036
@ the name of an image, or a URL. By default, the target is displayed
9631037
@ as the text of the hyperlink. But you can specify alternative text
9641038
@ after the target name separated by a "|" character.</p>
965
- @ <p>You can also link to internal anchor names using [#anchor-name], providing
1039
+ @ <p>You can also link to internal anchor names using [#anchor-name],
1040
+ @ providing
9661041
@ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
9671042
@ tag to your wiki page.</p></li>
9681043
@ <li> <p><span class="wikiruleHead">HTML</span>.
9691044
@ The following standard HTML elements may be used:
9701045
show_allowed_wiki_markup();
@@ -1166,11 +1241,12 @@
11661241
}else if( strncmp(g.argv[2],"delete",n)==0 ){
11671242
if( g.argc!=5 ){
11681243
usage("delete PAGENAME");
11691244
}
11701245
fossil_fatal("delete not yet implemented.");
1171
- }else if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){
1246
+ }else if(( strncmp(g.argv[2],"list",n)==0 )
1247
+ || ( strncmp(g.argv[2],"ls",n)==0 )){
11721248
Stmt q;
11731249
db_prepare(&q,
11741250
"SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
11751251
" ORDER BY lower(tagname) /*sort*/"
11761252
);
11771253
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,106 @@
198 if( localUser ){
199 return 0;
200 }
201 return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
204 /*
205 ** WEBPAGE: wiki
206 ** URL: /wiki?name=PAGENAME
207 */
@@ -208,10 +304,11 @@
208 void wiki_page(void){
209 char *zTag;
210 int rid = 0;
211 int isSandbox;
212 char *zUuid;
 
213 Blob wiki;
214 Manifest *pWiki = 0;
215 const char *zPageName;
216 const char *zMimetype = 0;
217 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,63 +315,35 @@
218
219 login_check_credentials();
220 if( !g.perm.RdWiki ){ login_needed(); return; }
221 zPageName = P("name");
222 if( zPageName==0 ){
223 style_header("Wiki");
224 @ <ul>
225 { char *zWikiHomePageName = db_get("index-page",0);
226 if( zWikiHomePageName ){
227 @ <li> %z(href("%R%s",zWikiHomePageName))
228 @ %h(zWikiHomePageName)</a> wiki home page.</li>
229 }
230 }
231 { char *zHomePageName = db_get("project-name",0);
232 if( zHomePageName ){
233 @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
234 @ %h(zHomePageName)</a> project home page.</li>
235 }
236 }
237 @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
238 @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
239 @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
240 @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
241 @ to experiment.</li>
242 if( g.perm.NewWiki ){
243 @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
244 if( g.perm.Write ){
245 @ <li> Create a %z(href("%R/eventedit"))new event</a>.</li>
246 }
247 }
248 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
249 @ available on this server.</li>
250 if( g.perm.ModWiki ){
251 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
252 }
253 @ <li>
254 form_begin(0, "%R/wfind");
255 @ <div>Search wiki titles: <input type="text" name="title"/>
256 @ &nbsp; <input type="submit" /></div></form>
257 @ </li>
258 @ </ul>
259 style_footer();
260 return;
261 }
262 if( check_name(zPageName) ) return;
263 isSandbox = is_sandbox(zPageName);
264 if( isSandbox ){
 
265 zBody = db_get("sandbox",zBody);
266 zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
267 rid = 0;
268 }else{
269 zTag = mprintf("wiki-%s", zPageName);
270 rid = db_int(0,
271 "SELECT rid FROM tagxref"
272 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
273 " ORDER BY mtime DESC", zTag
274 );
275 free(zTag);
 
 
 
276 pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
277 if( pWiki ){
278 zBody = pWiki->zWiki;
279 zMimetype = pWiki->zMimetype;
280 }
@@ -314,10 +383,11 @@
314 g.zTop, zPageName);
315 }
316 }
317 style_set_current_page("%T?name=%T", g.zPath, zPageName);
318 style_header("%s", zPageName);
 
319 blob_init(&wiki, zBody, -1);
320 wiki_render_by_mimetype(&wiki, zMimetype);
321 blob_reset(&wiki);
322 attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
323 manifest_destroy(pWiki);
@@ -570,10 +640,11 @@
570 }else{
571 cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
572 }
573 }
574 style_header("Create A New Wiki Page");
 
575 @ <p>Rules for wiki page names:</p>
576 well_formed_wiki_name_rules();
577 form_begin(0, "%R/wikinew");
578 @ <p>Name of new wiki page:
579 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -846,11 +917,12 @@
846 */
847 void wiki_prepare_page_list( Stmt * pStmt ){
848 db_prepare(pStmt,
849 "SELECT"
850 " substr(tagname, 6) as name,"
851 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
 
852 " FROM tag WHERE tagname GLOB 'wiki-*'"
853 " ORDER BY lower(tagname) /*sort*/"
854 );
855 }
856 /*
@@ -870,10 +942,11 @@
870 if( showAll ){
871 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
872 }else{
873 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
874 }
 
875 @ <ul>
876 wiki_prepare_page_list(&q);
877 while( db_step(&q)==SQLITE_ROW ){
878 const char *zName = db_column_text(&q, 0);
879 int size = db_column_int(&q, 1);
@@ -935,11 +1008,12 @@
935 @ </ol>
936 @ <p>We call the first five rules above "wiki" formatting rules. The
937 @ last two rules are the HTML formatting rule.</p>
938 @ <h2>Formatting Rule Details</h2>
939 @ <ol>
940 @ <li> <p><span class="wikiruleHead">Paragraphs</span>. Any sequence of one or more blank lines forms
 
941 @ a paragraph break. Centered or right-justified paragraphs are not
942 @ supported by wiki markup, but you can do these things if you need them
943 @ using HTML.</p></li>
944 @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
945 @ A bullet list item is a line that begins with a single "*" character
@@ -960,11 +1034,12 @@
960 @ Text within square brackets ("[...]") becomes a hyperlink. The
961 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
962 @ the name of an image, or a URL. By default, the target is displayed
963 @ as the text of the hyperlink. But you can specify alternative text
964 @ after the target name separated by a "|" character.</p>
965 @ <p>You can also link to internal anchor names using [#anchor-name], providing
 
966 @ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
967 @ tag to your wiki page.</p></li>
968 @ <li> <p><span class="wikiruleHead">HTML</span>.
969 @ The following standard HTML elements may be used:
970 show_allowed_wiki_markup();
@@ -1166,11 +1241,12 @@
1166 }else if( strncmp(g.argv[2],"delete",n)==0 ){
1167 if( g.argc!=5 ){
1168 usage("delete PAGENAME");
1169 }
1170 fossil_fatal("delete not yet implemented.");
1171 }else if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){
 
1172 Stmt q;
1173 db_prepare(&q,
1174 "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
1175 " ORDER BY lower(tagname) /*sort*/"
1176 );
1177
--- src/wiki.c
+++ src/wiki.c
@@ -198,10 +198,106 @@
198 if( localUser ){
199 return 0;
200 }
201 return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1;
202 }
203
204 /* Standard submenu items for wiki pages */
205 #define W_SRCH 0x00001
206 #define W_LIST 0x00002
207 #define W_HELP 0x00004
208 #define W_NEW 0x00008
209 #define W_BLOG 0x00010
210 #define W_SANDBOX 0x00020
211 #define W_ALL 0x0001f
212 #define W_ALL_BUT(x) (W_ALL&~(x))
213
214 /*
215 ** Add some standard submenu elements for wiki screens.
216 */
217 static void wiki_standard_submenu(unsigned int ok){
218 if( (ok & W_SRCH)!=0 && search_restrict(SRCH_WIKI)!=0 ){
219 style_submenu_element("Search","Search","%R/wikisrch");
220 }
221 if( (ok & W_LIST)!=0 ){
222 style_submenu_element("List","List","%R/wcontent");
223 }
224 if( (ok & W_HELP)!=0 ){
225 style_submenu_element("Help","Help","%R/wikihelp");
226 }
227 if( (ok & W_NEW)!=0 && g.perm.NewWiki ){
228 style_submenu_element("New","New","%R/wikinew");
229 }
230 #if 0
231 if( (ok & W_BLOG)!=0
232 #endif
233 if( (ok & W_SANDBOX)!=0 ){
234 style_submenu_element("Sandbox", "Sandbox", "%R/wiki?name=Sandbox");
235 }
236 }
237
238 /*
239 ** WEBPAGE: wikihelp
240 ** A generic landing page for wiki.
241 */
242 void wiki_helppage(void){
243 login_check_credentials();
244 if( !g.perm.RdWiki ){ login_needed(); return; }
245 style_header("Wiki Help");
246 wiki_standard_submenu(W_ALL_BUT(W_HELP));
247 @ <h2>Wiki Links</h2>
248 @ <ul>
249 { char *zWikiHomePageName = db_get("index-page",0);
250 if( zWikiHomePageName ){
251 @ <li> %z(href("%R%s",zWikiHomePageName))
252 @ %h(zWikiHomePageName)</a> wiki home page.</li>
253 }
254 }
255 { char *zHomePageName = db_get("project-name",0);
256 if( zHomePageName ){
257 @ <li> %z(href("%R/wiki?name=%t",zHomePageName))
258 @ %h(zHomePageName)</a> project home page.</li>
259 }
260 }
261 @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
262 @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
263 @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
264 @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
265 @ to experiment.</li>
266 if( g.perm.NewWiki ){
267 @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
268 if( g.perm.Write ){
269 @ <li> Create a %z(href("%R/eventedit"))new blog entry</a>.</li>
270 }
271 }
272 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
273 @ available on this server.</li>
274 if( g.perm.ModWiki ){
275 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
276 }
277 if( search_restrict(SRCH_WIKI)!=0 ){
278 @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
279 @ words</li>
280 }
281 @ </ul>
282 style_footer();
283 return;
284 }
285
286 /*
287 ** WEBPAGE: wikisrch
288 ** Usage: /wikisrch?s=PATTERN
289 **
290 ** Full-text search of all current wiki text
291 */
292 void wiki_srchpage(void){
293 login_check_credentials();
294 style_header("Wiki Search");
295 wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
296 search_screen(SRCH_WIKI, "wikisrch");
297 style_footer();
298 }
299
300 /*
301 ** WEBPAGE: wiki
302 ** URL: /wiki?name=PAGENAME
303 */
@@ -208,10 +304,11 @@
304 void wiki_page(void){
305 char *zTag;
306 int rid = 0;
307 int isSandbox;
308 char *zUuid;
309 unsigned submenuFlags = W_ALL;
310 Blob wiki;
311 Manifest *pWiki = 0;
312 const char *zPageName;
313 const char *zMimetype = 0;
314 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -218,63 +315,35 @@
315
316 login_check_credentials();
317 if( !g.perm.RdWiki ){ login_needed(); return; }
318 zPageName = P("name");
319 if( zPageName==0 ){
320 if( search_restrict(SRCH_WIKI)!=0 ){
321 wiki_srchpage();
322 }else{
323 wiki_helppage();
324 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325 return;
326 }
327 if( check_name(zPageName) ) return;
328 isSandbox = is_sandbox(zPageName);
329 if( isSandbox ){
330 submenuFlags &= ~W_SANDBOX;
331 zBody = db_get("sandbox",zBody);
332 zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki");
333 rid = 0;
334 }else{
335 const char *zUuid = P("id");
336 if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){
337 zTag = mprintf("wiki-%s", zPageName);
338 rid = db_int(0,
339 "SELECT rid FROM tagxref"
340 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
341 " ORDER BY mtime DESC", zTag
342 );
343 free(zTag);
344 }
345 pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
346 if( pWiki ){
347 zBody = pWiki->zWiki;
348 zMimetype = pWiki->zMimetype;
349 }
@@ -314,10 +383,11 @@
383 g.zTop, zPageName);
384 }
385 }
386 style_set_current_page("%T?name=%T", g.zPath, zPageName);
387 style_header("%s", zPageName);
388 wiki_standard_submenu(submenuFlags);
389 blob_init(&wiki, zBody, -1);
390 wiki_render_by_mimetype(&wiki, zMimetype);
391 blob_reset(&wiki);
392 attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
393 manifest_destroy(pWiki);
@@ -570,10 +640,11 @@
640 }else{
641 cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
642 }
643 }
644 style_header("Create A New Wiki Page");
645 wiki_standard_submenu(W_ALL_BUT(W_NEW));
646 @ <p>Rules for wiki page names:</p>
647 well_formed_wiki_name_rules();
648 form_begin(0, "%R/wikinew");
649 @ <p>Name of new wiki page:
650 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
@@ -846,11 +917,12 @@
917 */
918 void wiki_prepare_page_list( Stmt * pStmt ){
919 db_prepare(pStmt,
920 "SELECT"
921 " substr(tagname, 6) as name,"
922 " (SELECT value FROM tagxref WHERE tagid=tag.tagid"
923 " ORDER BY mtime DESC) as tagXref"
924 " FROM tag WHERE tagname GLOB 'wiki-*'"
925 " ORDER BY lower(tagname) /*sort*/"
926 );
927 }
928 /*
@@ -870,10 +942,11 @@
942 if( showAll ){
943 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
944 }else{
945 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
946 }
947 wiki_standard_submenu(W_ALL_BUT(W_LIST));
948 @ <ul>
949 wiki_prepare_page_list(&q);
950 while( db_step(&q)==SQLITE_ROW ){
951 const char *zName = db_column_text(&q, 0);
952 int size = db_column_int(&q, 1);
@@ -935,11 +1008,12 @@
1008 @ </ol>
1009 @ <p>We call the first five rules above "wiki" formatting rules. The
1010 @ last two rules are the HTML formatting rule.</p>
1011 @ <h2>Formatting Rule Details</h2>
1012 @ <ol>
1013 @ <li> <p><span class="wikiruleHead">Paragraphs</span>.
1014 @ Any sequence of one or more blank lines forms
1015 @ a paragraph break. Centered or right-justified paragraphs are not
1016 @ supported by wiki markup, but you can do these things if you need them
1017 @ using HTML.</p></li>
1018 @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
1019 @ A bullet list item is a line that begins with a single "*" character
@@ -960,11 +1034,12 @@
1034 @ Text within square brackets ("[...]") becomes a hyperlink. The
1035 @ target can be a wiki page name, the artifact ID of a check-in or ticket,
1036 @ the name of an image, or a URL. By default, the target is displayed
1037 @ as the text of the hyperlink. But you can specify alternative text
1038 @ after the target name separated by a "|" character.</p>
1039 @ <p>You can also link to internal anchor names using [#anchor-name],
1040 @ providing
1041 @ you have added the necessary "&lt;a name='anchor-name'&gt;&lt;/a&gt;"
1042 @ tag to your wiki page.</p></li>
1043 @ <li> <p><span class="wikiruleHead">HTML</span>.
1044 @ The following standard HTML elements may be used:
1045 show_allowed_wiki_markup();
@@ -1166,11 +1241,12 @@
1241 }else if( strncmp(g.argv[2],"delete",n)==0 ){
1242 if( g.argc!=5 ){
1243 usage("delete PAGENAME");
1244 }
1245 fossil_fatal("delete not yet implemented.");
1246 }else if(( strncmp(g.argv[2],"list",n)==0 )
1247 || ( strncmp(g.argv[2],"ls",n)==0 )){
1248 Stmt q;
1249 db_prepare(&q,
1250 "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
1251 " ORDER BY lower(tagname) /*sort*/"
1252 );
1253
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2092,9 +2092,81 @@
20922092
for(i=2; i<g.argc; i++){
20932093
blob_read_from_file(&in, g.argv[i]);
20942094
blob_zero(&out);
20952095
htmlTidy(blob_str(&in), &out);
20962096
blob_reset(&in);
2097
+ fossil_puts(blob_str(&out), 0);
2098
+ blob_reset(&out);
2099
+ }
2100
+}
2101
+
2102
+/*
2103
+** Remove all HTML markup from the input text. The output written into
2104
+** pOut is pure text.
2105
+*/
2106
+void html_to_plaintext(const char *zIn, Blob *pOut){
2107
+ int n;
2108
+ int i, j;
2109
+ int nNL = 0; /* Number of \n characters at the end of pOut */
2110
+ int nWS = 0; /* True if pOut ends with whitespace */
2111
+ while( zIn[0] ){
2112
+ n = nextHtmlToken(zIn);
2113
+ if( zIn[0]=='<' && n>1 ){
2114
+ int isCloseTag;
2115
+ int eTag;
2116
+ int eType;
2117
+ char zTag[32];
2118
+ isCloseTag = zIn[1]=='/';
2119
+ for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
2120
+ zTag[i] = fossil_tolower(zIn[j]);
2121
+ }
2122
+ zTag[i] = 0;
2123
+ eTag = findTag(zTag);
2124
+ eType = aMarkup[eTag].iType;
2125
+ if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
2126
+ zIn += n;
2127
+ while( zIn[0] ){
2128
+ n = nextHtmlToken(zIn);
2129
+ if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
2130
+ zIn += n;
2131
+ }
2132
+ if( zIn[0]=='<' ) zIn += n;
2133
+ continue;
2134
+ }
2135
+ if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){
2136
+ if( nNL==0 ){
2137
+ blob_append(pOut, "\n", 1);
2138
+ nNL++;
2139
+ }
2140
+ nWS = 1;
2141
+ }
2142
+ }else if( fossil_isspace(zIn[0]) ){
2143
+ for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++;
2144
+ if( !nWS ){
2145
+ blob_append(pOut, nNL ? "\n" : " ", 1);
2146
+ nWS = 1;
2147
+ }
2148
+ }else{
2149
+ blob_append(pOut, zIn, n);
2150
+ nNL = nWS = 0;
2151
+ }
2152
+ zIn += n;
2153
+ }
2154
+ if( nNL==0 ) blob_append(pOut, "\n", 1);
2155
+}
2156
+
2157
+/*
2158
+** COMMAND: test-html-to-text
2159
+*/
2160
+void test_html_to_text(void){
2161
+ Blob in, out;
2162
+ int i;
2163
+
2164
+ for(i=2; i<g.argc; i++){
2165
+ blob_read_from_file(&in, g.argv[i]);
2166
+ blob_zero(&out);
2167
+ html_to_plaintext(blob_str(&in), &out);
2168
+ blob_reset(&in);
20972169
fossil_puts(blob_str(&out), 0);
20982170
blob_reset(&out);
20992171
}
21002172
}
21012173
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2092,9 +2092,81 @@
2092 for(i=2; i<g.argc; i++){
2093 blob_read_from_file(&in, g.argv[i]);
2094 blob_zero(&out);
2095 htmlTidy(blob_str(&in), &out);
2096 blob_reset(&in);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2097 fossil_puts(blob_str(&out), 0);
2098 blob_reset(&out);
2099 }
2100 }
2101
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2092,9 +2092,81 @@
2092 for(i=2; i<g.argc; i++){
2093 blob_read_from_file(&in, g.argv[i]);
2094 blob_zero(&out);
2095 htmlTidy(blob_str(&in), &out);
2096 blob_reset(&in);
2097 fossil_puts(blob_str(&out), 0);
2098 blob_reset(&out);
2099 }
2100 }
2101
2102 /*
2103 ** Remove all HTML markup from the input text. The output written into
2104 ** pOut is pure text.
2105 */
2106 void html_to_plaintext(const char *zIn, Blob *pOut){
2107 int n;
2108 int i, j;
2109 int nNL = 0; /* Number of \n characters at the end of pOut */
2110 int nWS = 0; /* True if pOut ends with whitespace */
2111 while( zIn[0] ){
2112 n = nextHtmlToken(zIn);
2113 if( zIn[0]=='<' && n>1 ){
2114 int isCloseTag;
2115 int eTag;
2116 int eType;
2117 char zTag[32];
2118 isCloseTag = zIn[1]=='/';
2119 for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
2120 zTag[i] = fossil_tolower(zIn[j]);
2121 }
2122 zTag[i] = 0;
2123 eTag = findTag(zTag);
2124 eType = aMarkup[eTag].iType;
2125 if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
2126 zIn += n;
2127 while( zIn[0] ){
2128 n = nextHtmlToken(zIn);
2129 if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
2130 zIn += n;
2131 }
2132 if( zIn[0]=='<' ) zIn += n;
2133 continue;
2134 }
2135 if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){
2136 if( nNL==0 ){
2137 blob_append(pOut, "\n", 1);
2138 nNL++;
2139 }
2140 nWS = 1;
2141 }
2142 }else if( fossil_isspace(zIn[0]) ){
2143 for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++;
2144 if( !nWS ){
2145 blob_append(pOut, nNL ? "\n" : " ", 1);
2146 nWS = 1;
2147 }
2148 }else{
2149 blob_append(pOut, zIn, n);
2150 nNL = nWS = 0;
2151 }
2152 zIn += n;
2153 }
2154 if( nNL==0 ) blob_append(pOut, "\n", 1);
2155 }
2156
2157 /*
2158 ** COMMAND: test-html-to-text
2159 */
2160 void test_html_to_text(void){
2161 Blob in, out;
2162 int i;
2163
2164 for(i=2; i<g.argc; i++){
2165 blob_read_from_file(&in, g.argv[i]);
2166 blob_zero(&out);
2167 html_to_plaintext(blob_str(&in), &out);
2168 blob_reset(&in);
2169 fossil_puts(blob_str(&out), 0);
2170 blob_reset(&out);
2171 }
2172 }
2173
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,11 +83,11 @@
8383
8484
# define the SQLite files, which need special flags on compile
8585
SQLITESRC=sqlite3.c
8686
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
8787
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88
-SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_WIN32_NO_ANSI
88
+SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_WIN32_NO_ANSI
8989
9090
# define the SQLite shell files, which need special flags on compile
9191
SQLITESHELLSRC=shell.c
9292
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
9393
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
9494
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,11 +83,11 @@
83
84 # define the SQLite files, which need special flags on compile
85 SQLITESRC=sqlite3.c
86 ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
87 SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88 SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_WIN32_NO_ANSI
89
90 # define the SQLite shell files, which need special flags on compile
91 SQLITESHELLSRC=shell.c
92 ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
93 SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
94
--- win/Makefile.PellesCGMake
+++ win/Makefile.PellesCGMake
@@ -83,11 +83,11 @@
83
84 # define the SQLite files, which need special flags on compile
85 SQLITESRC=sqlite3.c
86 ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
87 SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
88 SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_WIN32_NO_ANSI
89
90 # define the SQLite shell files, which need special flags on compile
91 SQLITESHELLSRC=shell.c
92 ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
93 SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
94
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,11 +24,11 @@
2424
CFLAGS = -o
2525
BCC = $(DMDIR)\bin\dmc $(CFLAGS)
2626
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
2727
LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
2828
29
-SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
29
+SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4
3030
3131
SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
3333
SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3434
3535
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,11 +24,11 @@
24 CFLAGS = -o
25 BCC = $(DMDIR)\bin\dmc $(CFLAGS)
26 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
27 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -24,11 +24,11 @@
24 CFLAGS = -o
25 BCC = $(DMDIR)\bin\dmc $(CFLAGS)
26 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
27 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4
30
31 SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -146,11 +146,11 @@
146146
#### The directories where the OpenSSL include and library files are located.
147147
# The recommended usage here is to use the Sysinternals junction tool
148148
# to create a hard link between an "openssl-1.x" sub-directory of the
149149
# Fossil source code directory and the target OpenSSL source directory.
150150
#
151
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
151
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
152152
OPENSSLINCDIR = $(OPENSSLDIR)/include
153153
OPENSSLLIBDIR = $(OPENSSLDIR)
154154
155155
#### Either the directory where the Tcl library is installed or the Tcl
156156
# source code directory resides (depending on the value of the macro
@@ -2024,10 +2024,11 @@
20242024
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
20252025
-DSQLITE_THREADSAFE=0 \
20262026
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
20272027
-DSQLITE_OMIT_DEPRECATED \
20282028
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
2029
+ -DSQLITE_ENABLE_FTS4 \
20292030
-DSQLITE_WIN32_NO_ANSI \
20302031
-D_HAVE__MINGW_H \
20312032
-DSQLITE_USE_MALLOC_H \
20322033
-DSQLITE_USE_MSIZE
20332034
20342035
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -146,11 +146,11 @@
146 #### The directories where the OpenSSL include and library files are located.
147 # The recommended usage here is to use the Sysinternals junction tool
148 # to create a hard link between an "openssl-1.x" sub-directory of the
149 # Fossil source code directory and the target OpenSSL source directory.
150 #
151 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
152 OPENSSLINCDIR = $(OPENSSLDIR)/include
153 OPENSSLLIBDIR = $(OPENSSLDIR)
154
155 #### Either the directory where the Tcl library is installed or the Tcl
156 # source code directory resides (depending on the value of the macro
@@ -2024,10 +2024,11 @@
2024 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
2025 -DSQLITE_THREADSAFE=0 \
2026 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
2027 -DSQLITE_OMIT_DEPRECATED \
2028 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
 
2029 -DSQLITE_WIN32_NO_ANSI \
2030 -D_HAVE__MINGW_H \
2031 -DSQLITE_USE_MALLOC_H \
2032 -DSQLITE_USE_MSIZE
2033
2034
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -146,11 +146,11 @@
146 #### The directories where the OpenSSL include and library files are located.
147 # The recommended usage here is to use the Sysinternals junction tool
148 # to create a hard link between an "openssl-1.x" sub-directory of the
149 # Fossil source code directory and the target OpenSSL source directory.
150 #
151 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
152 OPENSSLINCDIR = $(OPENSSLDIR)/include
153 OPENSSLLIBDIR = $(OPENSSLDIR)
154
155 #### Either the directory where the Tcl library is installed or the Tcl
156 # source code directory resides (depending on the value of the macro
@@ -2024,10 +2024,11 @@
2024 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
2025 -DSQLITE_THREADSAFE=0 \
2026 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
2027 -DSQLITE_OMIT_DEPRECATED \
2028 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
2029 -DSQLITE_ENABLE_FTS4 \
2030 -DSQLITE_WIN32_NO_ANSI \
2031 -D_HAVE__MINGW_H \
2032 -DSQLITE_USE_MALLOC_H \
2033 -DSQLITE_USE_MSIZE
2034
2035
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -146,11 +146,11 @@
146146
#### The directories where the OpenSSL include and library files are located.
147147
# The recommended usage here is to use the Sysinternals junction tool
148148
# to create a hard link between an "openssl-1.x" sub-directory of the
149149
# Fossil source code directory and the target OpenSSL source directory.
150150
#
151
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
151
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
152152
OPENSSLINCDIR = $(OPENSSLDIR)/include
153153
OPENSSLLIBDIR = $(OPENSSLDIR)
154154
155155
#### Either the directory where the Tcl library is installed or the Tcl
156156
# source code directory resides (depending on the value of the macro
157157
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -146,11 +146,11 @@
146 #### The directories where the OpenSSL include and library files are located.
147 # The recommended usage here is to use the Sysinternals junction tool
148 # to create a hard link between an "openssl-1.x" sub-directory of the
149 # Fossil source code directory and the target OpenSSL source directory.
150 #
151 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l
152 OPENSSLINCDIR = $(OPENSSLDIR)/include
153 OPENSSLLIBDIR = $(OPENSSLDIR)
154
155 #### Either the directory where the Tcl library is installed or the Tcl
156 # source code directory resides (depending on the value of the macro
157
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -146,11 +146,11 @@
146 #### The directories where the OpenSSL include and library files are located.
147 # The recommended usage here is to use the Sysinternals junction tool
148 # to create a hard link between an "openssl-1.x" sub-directory of the
149 # Fossil source code directory and the target OpenSSL source directory.
150 #
151 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2
152 OPENSSLINCDIR = $(OPENSSLDIR)/include
153 OPENSSLLIBDIR = $(OPENSSLDIR)
154
155 #### Either the directory where the Tcl library is installed or the Tcl
156 # source code directory resides (depending on the value of the macro
157
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -55,11 +55,11 @@
5555
5656
# Uncomment to enable Tcl support
5757
# FOSSIL_ENABLE_TCL = 1
5858
5959
!ifdef FOSSIL_ENABLE_SSL
60
-SSLDIR = $(B)\compat\openssl-1.0.1l
60
+SSLDIR = $(B)\compat\openssl-1.0.2
6161
SSLINCDIR = $(SSLDIR)\inc32
6262
SSLLIBDIR = $(SSLDIR)\out32
6363
SSLLFLAGS = /nologo /opt:ref /debug
6464
SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
6565
!if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
@@ -189,10 +189,11 @@
189189
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
190190
/DSQLITE_THREADSAFE=0 \
191191
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
192192
/DSQLITE_OMIT_DEPRECATED \
193193
/DSQLITE_ENABLE_EXPLAIN_COMMENTS \
194
+ /DSQLITE_ENABLE_FTS4 \
194195
/DSQLITE_WIN32_NO_ANSI
195196
196197
SHELL_OPTIONS = /Dmain=sqlite3_shell \
197198
/DSQLITE_OMIT_LOAD_EXTENSION=1 \
198199
/DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
199200
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -55,11 +55,11 @@
55
56 # Uncomment to enable Tcl support
57 # FOSSIL_ENABLE_TCL = 1
58
59 !ifdef FOSSIL_ENABLE_SSL
60 SSLDIR = $(B)\compat\openssl-1.0.1l
61 SSLINCDIR = $(SSLDIR)\inc32
62 SSLLIBDIR = $(SSLDIR)\out32
63 SSLLFLAGS = /nologo /opt:ref /debug
64 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
65 !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
@@ -189,10 +189,11 @@
189 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
190 /DSQLITE_THREADSAFE=0 \
191 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
192 /DSQLITE_OMIT_DEPRECATED \
193 /DSQLITE_ENABLE_EXPLAIN_COMMENTS \
 
194 /DSQLITE_WIN32_NO_ANSI
195
196 SHELL_OPTIONS = /Dmain=sqlite3_shell \
197 /DSQLITE_OMIT_LOAD_EXTENSION=1 \
198 /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
199
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -55,11 +55,11 @@
55
56 # Uncomment to enable Tcl support
57 # FOSSIL_ENABLE_TCL = 1
58
59 !ifdef FOSSIL_ENABLE_SSL
60 SSLDIR = $(B)\compat\openssl-1.0.2
61 SSLINCDIR = $(SSLDIR)\inc32
62 SSLLIBDIR = $(SSLDIR)\out32
63 SSLLFLAGS = /nologo /opt:ref /debug
64 SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib
65 !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64"
@@ -189,10 +189,11 @@
189 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
190 /DSQLITE_THREADSAFE=0 \
191 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
192 /DSQLITE_OMIT_DEPRECATED \
193 /DSQLITE_ENABLE_EXPLAIN_COMMENTS \
194 /DSQLITE_ENABLE_FTS4 \
195 /DSQLITE_WIN32_NO_ANSI
196
197 SHELL_OPTIONS = /Dmain=sqlite3_shell \
198 /DSQLITE_OMIT_LOAD_EXTENSION=1 \
199 /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \
200
+1 -1
--- www/build.wiki
+++ www/build.wiki
@@ -122,11 +122,11 @@
122122
the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
123123
first <a href="https://www.openssl.org/source/">download the official
124124
source code for OpenSSL</a> and extract it to an appropriately named
125125
"<b>openssl-X.Y.ZA</b>" subdirectory within the local
126126
[/tree?ci=trunk&name=compat | compat] directory (e.g.
127
-"<b>compat/openssl-1.0.1l</b>"), then make sure that some recent
127
+"<b>compat/openssl-1.0.2</b>"), then make sure that some recent
128128
<a href="http://www.perl.org/">Perl</a> binaries are installed locally,
129129
and finally run one of the following commands:
130130
<blockquote><pre>
131131
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
132132
</pre></blockquote>
133133
--- www/build.wiki
+++ www/build.wiki
@@ -122,11 +122,11 @@
122 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
123 first <a href="https://www.openssl.org/source/">download the official
124 source code for OpenSSL</a> and extract it to an appropriately named
125 "<b>openssl-X.Y.ZA</b>" subdirectory within the local
126 [/tree?ci=trunk&name=compat | compat] directory (e.g.
127 "<b>compat/openssl-1.0.1l</b>"), then make sure that some recent
128 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
129 and finally run one of the following commands:
130 <blockquote><pre>
131 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
132 </pre></blockquote>
133
--- www/build.wiki
+++ www/build.wiki
@@ -122,11 +122,11 @@
122 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
123 first <a href="https://www.openssl.org/source/">download the official
124 source code for OpenSSL</a> and extract it to an appropriately named
125 "<b>openssl-X.Y.ZA</b>" subdirectory within the local
126 [/tree?ci=trunk&name=compat | compat] directory (e.g.
127 "<b>compat/openssl-1.0.2</b>"), then make sure that some recent
128 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
129 and finally run one of the following commands:
130 <blockquote><pre>
131 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
132 </pre></blockquote>
133
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,27 @@
11
<title>Change Log</title>
22
3
+<h2>Changes For Version 1.31 (2015-??-??)</h2>
4
+ * Add direct Subversion import support to
5
+ [/help?cmd=import|fossil import]. (??)
6
+ * Add IPv6 support to [/help?cmd=sync|fossil sync] and
7
+ [/help?cmd=clone|fossil clone]
8
+ * Add more skins such as "San Francisco Modern" and "Eagle".
9
+ * Update SQLite to version 3.8.9. (??)
10
+ * Improve the display of the history of changes to a single
11
+ file. A [/help?cmd=rebuild|fossil rebuild] is needed for that.
12
+ * Enable ZIP and Tarball downloads for user "nobody" by default.
13
+ * Make the now() SQL function available in the
14
+ [/help?cmd=sqlite|fossil sqlite] command.
15
+ * Improve table sorting in various pages.
16
+ * Add support for setting environment variables from within CGI script
17
+ files.
18
+ * Enhance the Ad-Unit processing to allow a choice of two different
19
+ ad-units.
20
+ * During shutdown, check to see if the check-out database (".fslckout")
21
+ contains a lot of free space, and if it does, VACUUM it.
22
+
323
<h2>Changes For Version 1.30 (2015-01-19)</h2>
424
* Added the [/help?cmd=bundle|fossil bundle] command.
525
* Added the [/help?cmd=purge|fossil purge] command.
626
* Added the [/help?cmd=publish|fossil publish] command.
727
* Added the [/help?cmd=unpublished|fossil unpublished] command.
828
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,27 @@
1 <title>Change Log</title>
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3 <h2>Changes For Version 1.30 (2015-01-19)</h2>
4 * Added the [/help?cmd=bundle|fossil bundle] command.
5 * Added the [/help?cmd=purge|fossil purge] command.
6 * Added the [/help?cmd=publish|fossil publish] command.
7 * Added the [/help?cmd=unpublished|fossil unpublished] command.
8
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,27 @@
1 <title>Change Log</title>
2
3 <h2>Changes For Version 1.31 (2015-??-??)</h2>
4 * Add direct Subversion import support to
5 [/help?cmd=import|fossil import]. (??)
6 * Add IPv6 support to [/help?cmd=sync|fossil sync] and
7 [/help?cmd=clone|fossil clone]
8 * Add more skins such as "San Francisco Modern" and "Eagle".
9 * Update SQLite to version 3.8.9. (??)
10 * Improve the display of the history of changes to a single
11 file. A [/help?cmd=rebuild|fossil rebuild] is needed for that.
12 * Enable ZIP and Tarball downloads for user "nobody" by default.
13 * Make the now() SQL function available in the
14 [/help?cmd=sqlite|fossil sqlite] command.
15 * Improve table sorting in various pages.
16 * Add support for setting environment variables from within CGI script
17 files.
18 * Enhance the Ad-Unit processing to allow a choice of two different
19 ad-units.
20 * During shutdown, check to see if the check-out database (".fslckout")
21 contains a lot of free space, and if it does, VACUUM it.
22
23 <h2>Changes For Version 1.30 (2015-01-19)</h2>
24 * Added the [/help?cmd=bundle|fossil bundle] command.
25 * Added the [/help?cmd=purge|fossil purge] command.
26 * Added the [/help?cmd=publish|fossil publish] command.
27 * Added the [/help?cmd=unpublished|fossil unpublished] command.
28
+1 -1
--- www/index.wiki
+++ www/index.wiki
@@ -23,11 +23,11 @@
2323
<li> [/timeline | Recent changes]
2424
<li> [./faq.wiki | FAQ]
2525
<li> [./hacker-howto.wiki | Hacker How-To]
2626
<li> [./changes.wiki | Change Log]
2727
<li> [./hints.wiki | Tip &amp; Hints]
28
-<li> [./permutedindex.wiki | Documentation Index]
28
+<li> [./permutedindex.html | Documentation Index]
2929
<li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
3030
<li> Mailing list
3131
<ul>
3232
<li> [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | sign-up]
3333
<li> [http://www.mail-archive.com/[email protected] | archives]
3434
--- www/index.wiki
+++ www/index.wiki
@@ -23,11 +23,11 @@
23 <li> [/timeline | Recent changes]
24 <li> [./faq.wiki | FAQ]
25 <li> [./hacker-howto.wiki | Hacker How-To]
26 <li> [./changes.wiki | Change Log]
27 <li> [./hints.wiki | Tip &amp; Hints]
28 <li> [./permutedindex.wiki | Documentation Index]
29 <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
30 <li> Mailing list
31 <ul>
32 <li> [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | sign-up]
33 <li> [http://www.mail-archive.com/[email protected] | archives]
34
--- www/index.wiki
+++ www/index.wiki
@@ -23,11 +23,11 @@
23 <li> [/timeline | Recent changes]
24 <li> [./faq.wiki | FAQ]
25 <li> [./hacker-howto.wiki | Hacker How-To]
26 <li> [./changes.wiki | Change Log]
27 <li> [./hints.wiki | Tip &amp; Hints]
28 <li> [./permutedindex.html | Documentation Index]
29 <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
30 <li> Mailing list
31 <ul>
32 <li> [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | sign-up]
33 <li> [http://www.mail-archive.com/[email protected] | archives]
34
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -224,21 +224,22 @@
224224
225225
Some files require special C-preprocessor macro definitions.
226226
When compiling sqlite.c, the following macros are recommended:
227227
228228
* -DSQLITE_OMIT_LOAD_EXTENSION=1
229
+ * -DSQLITE_ENABLE_FTS4=1
229230
* -DSQLITE_ENABLE_LOCKING_STYLE=0
230231
* -DSQLITE_THREADSAFE=0
231232
* -DSQLITE_DEFAULT_FILE_FORMAT=4
232233
* -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
233234
234
-The first symbol definition above is required; the others
235
-are merely recommended. Extension loading is omitted
236
-as a security measure. Fossil is single-threaded so mutexing is disabled
237
-in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS
238
-option makes the output of "EXPLAIN" queries in the
239
-"[/help?cmd=sqlite3|fossil sql]" command much more readable.
235
+The first two symbol definitions above are required; the others are merely
236
+recommended. Extension loading is omitted as a security measure. FTS4 is
237
+needed for the search feature. Fossil is single-threaded so mutexing is
238
+disabled in SQLite as a performance enhancement. The
239
+SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries
240
+in the "[/help?cmd=sqlite3|fossil sql]" command much more readable.
240241
241242
When compiling the shell.c source file, these macros are required:
242243
243244
* -Dmain=sqlite3_main
244245
* -DSQLITE_OMIT_LOAD_EXTENSION=1
245246
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -224,21 +224,22 @@
224
225 Some files require special C-preprocessor macro definitions.
226 When compiling sqlite.c, the following macros are recommended:
227
228 * -DSQLITE_OMIT_LOAD_EXTENSION=1
 
229 * -DSQLITE_ENABLE_LOCKING_STYLE=0
230 * -DSQLITE_THREADSAFE=0
231 * -DSQLITE_DEFAULT_FILE_FORMAT=4
232 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
233
234 The first symbol definition above is required; the others
235 are merely recommended. Extension loading is omitted
236 as a security measure. Fossil is single-threaded so mutexing is disabled
237 in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS
238 option makes the output of "EXPLAIN" queries in the
239 "[/help?cmd=sqlite3|fossil sql]" command much more readable.
240
241 When compiling the shell.c source file, these macros are required:
242
243 * -Dmain=sqlite3_main
244 * -DSQLITE_OMIT_LOAD_EXTENSION=1
245
--- www/makefile.wiki
+++ www/makefile.wiki
@@ -224,21 +224,22 @@
224
225 Some files require special C-preprocessor macro definitions.
226 When compiling sqlite.c, the following macros are recommended:
227
228 * -DSQLITE_OMIT_LOAD_EXTENSION=1
229 * -DSQLITE_ENABLE_FTS4=1
230 * -DSQLITE_ENABLE_LOCKING_STYLE=0
231 * -DSQLITE_THREADSAFE=0
232 * -DSQLITE_DEFAULT_FILE_FORMAT=4
233 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
234
235 The first two symbol definitions above are required; the others are merely
236 recommended. Extension loading is omitted as a security measure. FTS4 is
237 needed for the search feature. Fossil is single-threaded so mutexing is
238 disabled in SQLite as a performance enhancement. The
239 SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries
240 in the "[/help?cmd=sqlite3|fossil sql]" command much more readable.
241
242 When compiling the shell.c source file, these macros are required:
243
244 * -Dmain=sqlite3_main
245 * -DSQLITE_OMIT_LOAD_EXTENSION=1
246
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -26,11 +26,11 @@
2626
<div class="mainmenu">
2727
<a href='/fossil/doc/trunk/www/index.wiki'>Home</a>
2828
<a href='/fossil/timeline?y=ci'>Timeline</a>
2929
<a href='/download.html'>Download</a>
3030
<a href='/fossil/dir?ci=trunk'>Code</a>
31
-<a href='/fossil/doc/trunk/www/permutedindex.wiki'>Documentation</a>
31
+<a href='/fossil/doc/trunk/www/permutedindex.html'>Documentation</a>
3232
<a href='/fossil/brlist'>Branches</a>
3333
<a href='/fossil/taglist'>Tags</a>
3434
<a href='/fossil/reportlist'>Tickets</a>
3535
</div>
3636
<div class="content">
3737
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -26,11 +26,11 @@
26 <div class="mainmenu">
27 <a href='/fossil/doc/trunk/www/index.wiki'>Home</a>
28 <a href='/fossil/timeline?y=ci'>Timeline</a>
29 <a href='/download.html'>Download</a>
30 <a href='/fossil/dir?ci=trunk'>Code</a>
31 <a href='/fossil/doc/trunk/www/permutedindex.wiki'>Documentation</a>
32 <a href='/fossil/brlist'>Branches</a>
33 <a href='/fossil/taglist'>Tags</a>
34 <a href='/fossil/reportlist'>Tickets</a>
35 </div>
36 <div class="content">
37
--- www/mkdownload.tcl
+++ www/mkdownload.tcl
@@ -26,11 +26,11 @@
26 <div class="mainmenu">
27 <a href='/fossil/doc/trunk/www/index.wiki'>Home</a>
28 <a href='/fossil/timeline?y=ci'>Timeline</a>
29 <a href='/download.html'>Download</a>
30 <a href='/fossil/dir?ci=trunk'>Code</a>
31 <a href='/fossil/doc/trunk/www/permutedindex.html'>Documentation</a>
32 <a href='/fossil/brlist'>Branches</a>
33 <a href='/fossil/taglist'>Tags</a>
34 <a href='/fossil/reportlist'>Tickets</a>
35 </div>
36 <div class="content">
37
+18 -10
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -1,11 +1,11 @@
11
#!/bin/sh
22
#
33
# Run this TCL script to generate a WIKI page that contains a
44
# permuted index of the various documentation files.
55
#
6
-# tclsh mkindex.tcl >permutedindex.wiki
6
+# tclsh mkindex.tcl >permutedindex.html
77
#
88
99
set doclist {
1010
adding_code.wiki {Adding New Features To Fossil}
1111
adding_code.wiki {Hacking Fossil}
@@ -77,26 +77,34 @@
7777
lappend permindex [list "$suffix &mdash; $prefix" $file]
7878
}
7979
}
8080
}
8181
set permindex [lsort -dict -index 0 $permindex]
82
-set out [open permutedindex.wiki w]
82
+set out [open permutedindex.html w]
8383
fconfigure $out -encoding utf-8 -translation lf
84
-puts $out "<title>Index Of Fossil Documentation</title>"
84
+puts $out \
85
+"<div class='fossil-doc' data-title='Index Of Fossil Documentation'>"
8586
puts $out {
87
+<center>
88
+<form action='../../../docsrch' method='GET'>
89
+<input type="text" name="s" size="40" autofocus>
90
+<input type="submit" value="Search Docs">
91
+</form>
92
+</center>
8693
<h2>Primary Documents:</h2>
8794
<ul>
88
-<li> [./quickstart.wiki | Quick-start Guide]
89
-<li> [./faq.wiki | FAQ]
90
-<li> [./build.wiki | Compiling and installing Fossil]
91
-<li> [../COPYRIGHT-BSD2.txt | License]
92
-<li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
93
-<li> [/help | Command-line help]
95
+<li> <a href='quickstart.wiki'>Quick-start Guide</a>
96
+<li> <a href='faq.wiki'>FAQ</a>
97
+<li> <a href='build.wiki'>Compiling and installing Fossil</a>
98
+<li> <a href='COPYRIGHT-BSD2.txt'>License</a>
99
+<li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
100
+book</a>
101
+<li> <a href='../../help'>Command-line help</a>
94102
</ul>
95103
<a name="pindex"></a>
96104
<h2>Permuted Index:</h2>
97105
<ul>}
98106
foreach entry $permindex {
99107
foreach {title file} $entry break
100108
puts $out "<li><a href=\"$file\">$title</a></li>"
101109
}
102
-puts $out "</ul>"
110
+puts $out "</ul></div>"
103111
104112
ADDED www/permutedindex.html
105113
DELETED www/permutedindex.wiki
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -1,11 +1,11 @@
1 #!/bin/sh
2 #
3 # Run this TCL script to generate a WIKI page that contains a
4 # permuted index of the various documentation files.
5 #
6 # tclsh mkindex.tcl >permutedindex.wiki
7 #
8
9 set doclist {
10 adding_code.wiki {Adding New Features To Fossil}
11 adding_code.wiki {Hacking Fossil}
@@ -77,26 +77,34 @@
77 lappend permindex [list "$suffix &mdash; $prefix" $file]
78 }
79 }
80 }
81 set permindex [lsort -dict -index 0 $permindex]
82 set out [open permutedindex.wiki w]
83 fconfigure $out -encoding utf-8 -translation lf
84 puts $out "<title>Index Of Fossil Documentation</title>"
 
85 puts $out {
 
 
 
 
 
 
86 <h2>Primary Documents:</h2>
87 <ul>
88 <li> [./quickstart.wiki | Quick-start Guide]
89 <li> [./faq.wiki | FAQ]
90 <li> [./build.wiki | Compiling and installing Fossil]
91 <li> [../COPYRIGHT-BSD2.txt | License]
92 <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book]
93 <li> [/help | Command-line help]
 
94 </ul>
95 <a name="pindex"></a>
96 <h2>Permuted Index:</h2>
97 <ul>}
98 foreach entry $permindex {
99 foreach {title file} $entry break
100 puts $out "<li><a href=\"$file\">$title</a></li>"
101 }
102 puts $out "</ul>"
103
104 DDED www/permutedindex.html
105 ELETED www/permutedindex.wiki
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -1,11 +1,11 @@
1 #!/bin/sh
2 #
3 # Run this TCL script to generate a WIKI page that contains a
4 # permuted index of the various documentation files.
5 #
6 # tclsh mkindex.tcl >permutedindex.html
7 #
8
9 set doclist {
10 adding_code.wiki {Adding New Features To Fossil}
11 adding_code.wiki {Hacking Fossil}
@@ -77,26 +77,34 @@
77 lappend permindex [list "$suffix &mdash; $prefix" $file]
78 }
79 }
80 }
81 set permindex [lsort -dict -index 0 $permindex]
82 set out [open permutedindex.html w]
83 fconfigure $out -encoding utf-8 -translation lf
84 puts $out \
85 "<div class='fossil-doc' data-title='Index Of Fossil Documentation'>"
86 puts $out {
87 <center>
88 <form action='../../../docsrch' method='GET'>
89 <input type="text" name="s" size="40" autofocus>
90 <input type="submit" value="Search Docs">
91 </form>
92 </center>
93 <h2>Primary Documents:</h2>
94 <ul>
95 <li> <a href='quickstart.wiki'>Quick-start Guide</a>
96 <li> <a href='faq.wiki'>FAQ</a>
97 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
98 <li> <a href='COPYRIGHT-BSD2.txt'>License</a>
99 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
100 book</a>
101 <li> <a href='../../help'>Command-line help</a>
102 </ul>
103 <a name="pindex"></a>
104 <h2>Permuted Index:</h2>
105 <ul>}
106 foreach entry $permindex {
107 foreach {title file} $entry break
108 puts $out "<li><a href=\"$file\">$title</a></li>"
109 }
110 puts $out "</ul></div>"
111
112 DDED www/permutedindex.html
113 ELETED www/permutedindex.wiki
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -1,8 +1,5 @@
1
-<title>p'>Site Mapname GLOBLicCommanp'>Site Mmap'>Site Ma[./quickstart.wiki | Quick-start Guide]
2
-<li> [./faq.wiki | FAQ]
3
-<li> [./build.wiki | g ruleswiki_rules'>Wikirmatting]
4
-<li> [../COPYRIGHTmand-line md_ru | Jim Schimpf's book]
5
-<li> nd-line h2>Canonical Index:</h2>
1
+p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting rulcenter>
2
+<formte Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ru/center ruiki formatting rulessitema/centeremap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wikirmatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>e Mapname GLOBLOB nLicCommand-line h2>Canonical Index:</h2>
63
(lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
74
</ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
85
</ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
@@ -10,4 +7,4 @@
107
</ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
118
u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
129
</ul></div>
13
-dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
10
+dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -1,8 +1,5 @@
1 <title>p'>Site Mapname GLOBLicCommanp'>Site Mmap'>Site Ma[./quickstart.wiki | Quick-start Guide]
2 <li> [./faq.wiki | FAQ]
3 <li> [./build.wiki | g ruleswiki_rules'>Wikirmatting]
4 <li> [../COPYRIGHTmand-line md_ru | Jim Schimpf's book]
5 <li> nd-line h2>Canonical Index:</h2>
6 (lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
7 </ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
8 </ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
@@ -10,4 +7,4 @@
10 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
11 u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
12 </ul></div>
13 dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -1,8 +1,5 @@
1 p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting rulcenter>
2 <formte Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ru/center ruiki formatting rulessitema/centeremap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wikirmatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>e Mapname GLOBLOB nLicCommand-line h2>Canonical Index:</h2>
 
 
 
3 (lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
4 </ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
5 </ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
@@ -10,4 +7,4 @@
7 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
8 u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
9 </ul></div>
10 dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -0,0 +1,10 @@
1
+p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting rulcenter>
2
+<formte Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ru/center ruiki formatting rulessitema/centeremap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wikirmatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>e Mapname GLOBLOB nLicCommand-line h2>Canonical Index:</h2>
3
+(lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
4
+</ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
5
+</ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
6
+</ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wikiline webpage-ex.Guideould UseWhlessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi L-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
7
+</ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
8
+u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
9
+</ul></div>
10
+dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
--- a/www/permutedindex.html
+++ b/www/permutedindex.html
@@ -0,0 +1,10 @@
1 p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting rulcenter>
2 <formte Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ru/center ruiki formatting rulessitema/centeremap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wikirmatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>e Mapname GLOBLOB nLicCommand-line h2>Canonical Index:</h2>
3 (lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
4 </ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
5 </ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
6 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wikiline webpage-ex.Guideould UseWhlessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi L-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
7 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
8 u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
9 </ul></div>
10 dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
D www/permutedindex.wiki
-13
--- a/www/permutedindex.wiki
+++ b/www/permutedindex.wiki
@@ -1,13 +0,0 @@
1
-<title>p'>Site Mapname GLOBLicCommanp'>Site Mmap'>Site Ma[./quickstart.wiki | Quick-start Guide]
2
-<li> [./faq.wiki | FAQ]
3
-<li> [./build.wiki | g ruleswiki_rules'>Wikirmatting]
4
-<li> [../COPYRIGHTmand-line md_ru | Jim Schimpf's book]
5
-<li> nd-line h2>Canonical Index:</h2>
6
-(lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
7
-</ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
8
-</ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
9
-</ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wikiline webpage-ex.Guideould UseWhlessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi L-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
10
-</ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
11
-u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
12
-</ul></div>
13
-dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.wiki
+++ b/www/permutedindex.wiki
@@ -1,13 +0,0 @@
1 <title>p'>Site Mapname GLOBLicCommanp'>Site Mmap'>Site Ma[./quickstart.wiki | Quick-start Guide]
2 <li> [./faq.wiki | FAQ]
3 <li> [./build.wiki | g ruleswiki_rules'>Wikirmatting]
4 <li> [../COPYRIGHTmand-line md_ru | Jim Schimpf's book]
5 <li> nd-line h2>Canonical Index:</h2>
6 (lketsicket LicCommand-line md_rul LicCommand-name GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikia><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
7 </ul>_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Shd_rulesdatep'>ncep md_rulesould UseWhy</a></li>
8 </ul></div>iinutes.wiki"wn formatting rules md_rulesdatepiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a><cCommand-line md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formaUse<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
9 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f p'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wikiline webpage-ex.Guideould UseWhlessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCS_ticketicket ticketsicket LicCommand-line md_rul LicCommand-lin Customizing thints.wiki">TipdatA dding_code.wiki">Adding >Site Mapname GLOBLicCp'>Site Ma"><b>Wiki In Pag.wiki"><b>Wiki In PagabfitVersion NameCheck-in Afossil-v-git.wiki">Versus webui.wiki">Web Interface GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi L-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wiki"><b>Wiki In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
10 </ul></div>iinutes.wiki">wn formatting rules md_rulesdatep'>ncep md_rulesdatep'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLicCommand-line md_rules'>Markdown f /ul></div>
11 u Should UseWhy</a><'>Site Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBLOB nLi LicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting ruleste Mapname GLOBLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> Commandqu./../../help"> CommandquoQuot In Pagaboutcgi.wiki"> CGIYou Should UseWhy</a></li>
12 </ul></div>
13 dates And Usage Hin 5 LicCommand-line LicCommand-line md_rulesdatep'>Site Maprkdown formattin Taggrul GLOBLOB nLicCommand-line md_rules'>Markdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Maprkdown formatting ruleswiki_rules'>Wiki formatting rulessitemap'>Site Mapname GLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../help"> CommandquoQuotes:Why You Should Use<b>Wikiwikitheory.wikLOBMarkdThe Fossil DVCCheck-in And Version Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCSion Namesheckin.wiki">Check-in Checklistheckiniki"><b>Unversioned FilefCheck-innversioned Filefivemin SSL withenvantibot.wiki">against opts.md">Variables and LicCommand-line md_ruChild Projectst.wiki">Versus webui.wiki">Web Interface LicCommand-line md_rul LicCommand-line webpage-ex.md"><b>Webpage Example../../../htheory1mmand-l DVCS
--- a/www/permutedindex.wiki
+++ b/www/permutedindex.wiki
@@ -1,13 +0,0 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -387,9 +387,9 @@
387387
388388
<h2>More Hints</h2>
389389
390390
<p>A [/help | complete list of commands] is available, as is the
391391
[./hints.wiki|helpful hints] document. See the
392
- [./permutedindex.wiki#pindex|permuted index] for additional
392
+ [./permutedindex.html#pindex|permuted index] for additional
393393
documentation.
394394
395395
<p>Explore and have fun!</p>
396396
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -387,9 +387,9 @@
387
388 <h2>More Hints</h2>
389
390 <p>A [/help | complete list of commands] is available, as is the
391 [./hints.wiki|helpful hints] document. See the
392 [./permutedindex.wiki#pindex|permuted index] for additional
393 documentation.
394
395 <p>Explore and have fun!</p>
396
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -387,9 +387,9 @@
387
388 <h2>More Hints</h2>
389
390 <p>A [/help | complete list of commands] is available, as is the
391 [./hints.wiki|helpful hints] document. See the
392 [./permutedindex.html#pindex|permuted index] for additional
393 documentation.
394
395 <p>Explore and have fun!</p>
396
+23 -6
--- www/sync.wiki
+++ www/sync.wiki
@@ -384,11 +384,11 @@
384384
385385
<blockquote>
386386
<b>reqconfig</b> <i>configuration-name</i>
387387
</blockquote>
388388
389
-<p>As of this writing ([2010-11-12]), the configuration-name must be one of the
389
+<p>As of [/timeline?r=trunk&c=2015-01-24+00%3A17%3A55&n=20|2015-01-24], the configuration-name must be one of the
390390
following values:
391391
392392
<center><table border=0>
393393
<tr><td valign="top">
394394
<ul>
@@ -395,25 +395,42 @@
395395
<li> css
396396
<li> header
397397
<li> footer
398398
<li> logo-mimetype
399399
<li> logo-image
400
-<li> project-name
401
-<li> project-description
402
-<li> manifest
400
+<li> background-mimetype
401
+<li> background-image
403402
<li> index-page
404
-<ul></td><td valign="top"><ul>
405403
<li> timeline-block-markup
406404
<li> timeline-max-comment
407405
<li> timeline-plaintext
406
+<ul></td><td valign="top"><ul>
407
+<li> adunit
408
+<li> adunit-omit-if-admin
409
+<li> adunit-omit-if-user
410
+<li> white-foreground
411
+<li> project-name
412
+<li> short-project-name
413
+<li> project-description
414
+<li> index-page
415
+<li> manifest
416
+<li> binary-glob
417
+<li> clean-glob
418
+<ul></td><td valign="top"><ul>
419
+<li> ignore-glob
420
+<li> keep-glob
421
+<li> crnl-glob
422
+<li> encoding-glob
423
+<li> empty-dirs
424
+<li> allow-symlinks
408425
<li> ticket-table
409426
<li> ticket-common
410427
<li> ticket-change
411428
<li> ticket-newpage
412429
<li> ticket-viewpage
413
-<li> ticket-editpage
414430
<ul></td><td valign="top"><ul>
431
+<li> ticket-editpage
415432
<li> ticket-reportlist
416433
<li> ticket-report-template
417434
<li> ticket-key-template
418435
<li> ticket-title-expr
419436
<li> ticket-closed-expr
420437
--- www/sync.wiki
+++ www/sync.wiki
@@ -384,11 +384,11 @@
384
385 <blockquote>
386 <b>reqconfig</b> <i>configuration-name</i>
387 </blockquote>
388
389 <p>As of this writing ([2010-11-12]), the configuration-name must be one of the
390 following values:
391
392 <center><table border=0>
393 <tr><td valign="top">
394 <ul>
@@ -395,25 +395,42 @@
395 <li> css
396 <li> header
397 <li> footer
398 <li> logo-mimetype
399 <li> logo-image
400 <li> project-name
401 <li> project-description
402 <li> manifest
403 <li> index-page
404 <ul></td><td valign="top"><ul>
405 <li> timeline-block-markup
406 <li> timeline-max-comment
407 <li> timeline-plaintext
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408 <li> ticket-table
409 <li> ticket-common
410 <li> ticket-change
411 <li> ticket-newpage
412 <li> ticket-viewpage
413 <li> ticket-editpage
414 <ul></td><td valign="top"><ul>
 
415 <li> ticket-reportlist
416 <li> ticket-report-template
417 <li> ticket-key-template
418 <li> ticket-title-expr
419 <li> ticket-closed-expr
420
--- www/sync.wiki
+++ www/sync.wiki
@@ -384,11 +384,11 @@
384
385 <blockquote>
386 <b>reqconfig</b> <i>configuration-name</i>
387 </blockquote>
388
389 <p>As of [/timeline?r=trunk&c=2015-01-24+00%3A17%3A55&n=20|2015-01-24], the configuration-name must be one of the
390 following values:
391
392 <center><table border=0>
393 <tr><td valign="top">
394 <ul>
@@ -395,25 +395,42 @@
395 <li> css
396 <li> header
397 <li> footer
398 <li> logo-mimetype
399 <li> logo-image
400 <li> background-mimetype
401 <li> background-image
 
402 <li> index-page
 
403 <li> timeline-block-markup
404 <li> timeline-max-comment
405 <li> timeline-plaintext
406 <ul></td><td valign="top"><ul>
407 <li> adunit
408 <li> adunit-omit-if-admin
409 <li> adunit-omit-if-user
410 <li> white-foreground
411 <li> project-name
412 <li> short-project-name
413 <li> project-description
414 <li> index-page
415 <li> manifest
416 <li> binary-glob
417 <li> clean-glob
418 <ul></td><td valign="top"><ul>
419 <li> ignore-glob
420 <li> keep-glob
421 <li> crnl-glob
422 <li> encoding-glob
423 <li> empty-dirs
424 <li> allow-symlinks
425 <li> ticket-table
426 <li> ticket-common
427 <li> ticket-change
428 <li> ticket-newpage
429 <li> ticket-viewpage
 
430 <ul></td><td valign="top"><ul>
431 <li> ticket-editpage
432 <li> ticket-reportlist
433 <li> ticket-report-template
434 <li> ticket-key-template
435 <li> ticket-title-expr
436 <li> ticket-closed-expr
437

Keyboard Shortcuts

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