Fossil SCM
merge trunk
Commit
9e7ea6a20fd81a1487ba5fc16902e09ff04c535c
Parent
4aa351bba8350ca…
52 files changed
+1
-1
+224
-121
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+2
-2
+1
-1
+1
-1
+1
-1
+5
+1
-1
+3
-3
+139
-88
+424
-289
+2
-2
+7
-2
+2
-1
+3
-2
+5
+11
-3
+3
+2
-2
+1
-1
+1130
-65
+82
-8
+2
-2
+1
+109
-116
+2
-2
+39
-2
+57
+1
-1
+124
-48
+72
+1
-1
+1
-1
+2
-1
+1
-1
+2
-1
+1
-1
+20
+1
-1
+7
-6
+1
-1
+18
-10
+3
-6
+10
-13
+1
-1
+23
-6
~
auto.def
~
autosetup/jimsh0.c
~
skins/black_and_white/header.txt
~
skins/default/header.txt
~
skins/eagle/css.txt
~
skins/eagle/header.txt
~
skins/enhanced1/header.txt
~
skins/etienne1/header.txt
~
skins/khaki/header.txt
~
skins/plain_gray/header.txt
~
skins/rounded1/header.txt
~
src/allrepo.c
~
src/clone.c
~
src/configure.c
~
src/db.c
~
src/doc.c
~
src/glob.c
~
src/json_config.c
~
src/main.mk
~
src/makemake.tcl
~
src/manifest.c
~
src/rebuild.c
~
src/report.c
~
src/rss.c
~
src/schema.c
~
src/search.c
~
src/setup.c
~
src/skins.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/tkt.c
~
src/url.c
~
src/wiki.c
~
src/wikiformat.c
~
win/Makefile.PellesCGMake
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
www/build.wiki
~
www/changes.wiki
~
www/index.wiki
~
www/makefile.wiki
~
www/mkdownload.tcl
~
www/mkindex.tcl
~
www/permutedindex.html
+
www/permutedindex.html
-
www/permutedindex.wiki
~
www/quickstart.wiki
~
www/sync.wiki
M
auto.def
+1
-1
| --- auto.def | ||
| +++ auto.def | ||
| @@ -68,11 +68,11 @@ | ||
| 68 | 68 | |
| 69 | 69 | find_internal_sqlite |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | if {[string match *-solaris* [get-define host]]} { |
| 73 | - define-append EXTRA_CFLAGS -D_XOPEN_SOURCE=500 | |
| 73 | + define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} | |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | if {[opt-bool fossil-debug]} { |
| 77 | 77 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 78 | 78 | msg-result "Debugging support enabled" |
| 79 | 79 |
| --- 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 @@ | ||
| 38 | 38 | #define TCL_PLATFORM_PLATFORM "unix" |
| 39 | 39 | #define TCL_PLATFORM_PATH_SEPARATOR ":" |
| 40 | 40 | #define HAVE_VFORK |
| 41 | 41 | #define HAVE_WAITPID |
| 42 | 42 | #define HAVE_ISATTY |
| 43 | +#define HAVE_MKSTEMP | |
| 44 | +#define HAVE_LINK | |
| 43 | 45 | #define HAVE_SYS_TIME_H |
| 44 | 46 | #define HAVE_DIRENT_H |
| 45 | 47 | #define HAVE_UNISTD_H |
| 46 | 48 | #endif |
| 47 | -#define JIM_VERSION 75 | |
| 49 | +#define JIM_VERSION 76 | |
| 48 | 50 | #ifndef JIM_WIN32COMPAT_H |
| 49 | 51 | #define JIM_WIN32COMPAT_H |
| 50 | 52 | |
| 51 | 53 | |
| 52 | 54 | |
| @@ -113,10 +115,11 @@ | ||
| 113 | 115 | int closedir(DIR *dir); |
| 114 | 116 | struct dirent *readdir(DIR *dir); |
| 115 | 117 | |
| 116 | 118 | #elif defined(__MINGW32__) |
| 117 | 119 | |
| 120 | +#include <stdlib.h> | |
| 118 | 121 | #define strtod __strtod |
| 119 | 122 | |
| 120 | 123 | #endif |
| 121 | 124 | |
| 122 | 125 | #endif |
| @@ -1048,29 +1051,48 @@ | ||
| 1048 | 1051 | "\n" |
| 1049 | 1052 | "\n" |
| 1050 | 1053 | "\n" |
| 1051 | 1054 | "proc _jimsh_init {} {\n" |
| 1052 | 1055 | " rename _jimsh_init {}\n" |
| 1056 | +" global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n" | |
| 1053 | 1057 | "\n" |
| 1054 | 1058 | "\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" | |
| 1059 | 1072 | "\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" | |
| 1061 | 1082 | " foreach src {.jimrc jimrc.tcl} {\n" |
| 1062 | 1083 | " if {[file exists [env HOME]/$src]} {\n" |
| 1063 | 1084 | " uplevel #0 source [env HOME]/$src\n" |
| 1064 | 1085 | " break\n" |
| 1065 | 1086 | " }\n" |
| 1066 | 1087 | " }\n" |
| 1067 | 1088 | " }\n" |
| 1089 | +" return \"\"\n" | |
| 1068 | 1090 | "}\n" |
| 1069 | 1091 | "\n" |
| 1070 | 1092 | "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" | |
| 1072 | 1094 | "}\n" |
| 1073 | 1095 | "\n" |
| 1074 | 1096 | "_jimsh_init\n" |
| 1075 | 1097 | ); |
| 1076 | 1098 | } |
| @@ -1091,11 +1113,11 @@ | ||
| 1091 | 1113 | "\n" |
| 1092 | 1114 | "\n" |
| 1093 | 1115 | "proc glob.globdir {dir pattern} {\n" |
| 1094 | 1116 | " if {[file exists $dir/$pattern]} {\n" |
| 1095 | 1117 | "\n" |
| 1096 | -" return $pattern\n" | |
| 1118 | +" return [list $pattern]\n" | |
| 1097 | 1119 | " }\n" |
| 1098 | 1120 | "\n" |
| 1099 | 1121 | " set result {}\n" |
| 1100 | 1122 | " set files [readdir $dir]\n" |
| 1101 | 1123 | " lappend files . ..\n" |
| @@ -1153,11 +1175,11 @@ | ||
| 1153 | 1175 | " }\n" |
| 1154 | 1176 | "\n" |
| 1155 | 1177 | " foreach old $oldexp {\n" |
| 1156 | 1178 | " lappend newexp $old$suf\n" |
| 1157 | 1179 | " }\n" |
| 1158 | -" linsert $newexp 0 $rest\n" | |
| 1180 | +" list $rest {*}$newexp\n" | |
| 1159 | 1181 | "}\n" |
| 1160 | 1182 | "\n" |
| 1161 | 1183 | "\n" |
| 1162 | 1184 | "\n" |
| 1163 | 1185 | "proc glob.glob {base pattern} {\n" |
| @@ -1200,10 +1222,11 @@ | ||
| 1200 | 1222 | "\n" |
| 1201 | 1223 | "\n" |
| 1202 | 1224 | "proc glob {args} {\n" |
| 1203 | 1225 | " set nocomplain 0\n" |
| 1204 | 1226 | " set base \"\"\n" |
| 1227 | +" set tails 0\n" | |
| 1205 | 1228 | "\n" |
| 1206 | 1229 | " set n 0\n" |
| 1207 | 1230 | " foreach arg $args {\n" |
| 1208 | 1231 | " if {[info exists param]} {\n" |
| 1209 | 1232 | " set $param $arg\n" |
| @@ -1217,21 +1240,20 @@ | ||
| 1217 | 1240 | " set param base\n" |
| 1218 | 1241 | " }\n" |
| 1219 | 1242 | " -n* {\n" |
| 1220 | 1243 | " set nocomplain 1\n" |
| 1221 | 1244 | " }\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" | |
| 1228 | 1247 | " }\n" |
| 1229 | 1248 | " -- {\n" |
| 1230 | 1249 | " incr n\n" |
| 1231 | 1250 | " break\n" |
| 1232 | 1251 | " }\n" |
| 1252 | +" -* {\n" | |
| 1253 | +" return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n" | |
| 1254 | +" }\n" | |
| 1233 | 1255 | " * {\n" |
| 1234 | 1256 | " break\n" |
| 1235 | 1257 | " }\n" |
| 1236 | 1258 | " }\n" |
| 1237 | 1259 | " incr n\n" |
| @@ -1245,29 +1267,35 @@ | ||
| 1245 | 1267 | "\n" |
| 1246 | 1268 | " set args [lrange $args $n end]\n" |
| 1247 | 1269 | "\n" |
| 1248 | 1270 | " set result {}\n" |
| 1249 | 1271 | " foreach pattern $args {\n" |
| 1250 | -" set pattern [string map {\n" | |
| 1272 | +" set escpattern [string map {\n" | |
| 1251 | 1273 | " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n" |
| 1252 | 1274 | " } $pattern]\n" |
| 1253 | -" set patexps [lassign [glob.explode $pattern] rest]\n" | |
| 1275 | +" set patexps [lassign [glob.explode $escpattern] rest]\n" | |
| 1254 | 1276 | " if {$rest ne \"\"} {\n" |
| 1255 | 1277 | " return -code error \"unmatched close brace in glob pattern\"\n" |
| 1256 | 1278 | " }\n" |
| 1257 | 1279 | " foreach patexp $patexps {\n" |
| 1258 | 1280 | " set patexp [string map {\n" |
| 1259 | 1281 | " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n" |
| 1260 | 1282 | " } $patexp]\n" |
| 1261 | 1283 | " 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" | |
| 1263 | 1290 | " }\n" |
| 1264 | 1291 | " }\n" |
| 1265 | 1292 | " }\n" |
| 1266 | 1293 | "\n" |
| 1267 | 1294 | " 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" | |
| 1269 | 1297 | " }\n" |
| 1270 | 1298 | "\n" |
| 1271 | 1299 | " return $result\n" |
| 1272 | 1300 | "}\n" |
| 1273 | 1301 | ); |
| @@ -1348,11 +1376,11 @@ | ||
| 1348 | 1376 | "\n" |
| 1349 | 1377 | " lappend stacktrace {*}[stacktrace 1]\n" |
| 1350 | 1378 | " }\n" |
| 1351 | 1379 | " lassign $stacktrace p f l\n" |
| 1352 | 1380 | " if {$f ne \"\"} {\n" |
| 1353 | -" set result \"Runtime Error: $f:$l: \"\n" | |
| 1381 | +" set result \"$f:$l: Error: \"\n" | |
| 1354 | 1382 | " }\n" |
| 1355 | 1383 | " append result \"$msg\\n\"\n" |
| 1356 | 1384 | " append result [stackdump $stacktrace]\n" |
| 1357 | 1385 | "\n" |
| 1358 | 1386 | "\n" |
| @@ -1360,22 +1388,13 @@ | ||
| 1360 | 1388 | "}\n" |
| 1361 | 1389 | "\n" |
| 1362 | 1390 | "\n" |
| 1363 | 1391 | "\n" |
| 1364 | 1392 | "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" | |
| 1375 | 1395 | " }\n" |
| 1376 | -" return \"\"\n" | |
| 1377 | 1396 | "}\n" |
| 1378 | 1397 | "\n" |
| 1379 | 1398 | "\n" |
| 1380 | 1399 | "proc {dict with} {&dictVar {args key} script} {\n" |
| 1381 | 1400 | " set keys {}\n" |
| @@ -1587,11 +1606,11 @@ | ||
| 1587 | 1606 | " try {\n" |
| 1588 | 1607 | " if {$force ni {{} -force}} {\n" |
| 1589 | 1608 | " error \"bad option \\\"$force\\\": should be -force\"\n" |
| 1590 | 1609 | " }\n" |
| 1591 | 1610 | "\n" |
| 1592 | -" set in [open $source]\n" | |
| 1611 | +" set in [open $source rb]\n" | |
| 1593 | 1612 | "\n" |
| 1594 | 1613 | " if {[file exists $target]} {\n" |
| 1595 | 1614 | " if {$force eq \"\"} {\n" |
| 1596 | 1615 | " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n" |
| 1597 | 1616 | " }\n" |
| @@ -1605,11 +1624,11 @@ | ||
| 1605 | 1624 | " file stat $target ts\n" |
| 1606 | 1625 | " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n" |
| 1607 | 1626 | " return\n" |
| 1608 | 1627 | " }\n" |
| 1609 | 1628 | " }\n" |
| 1610 | -" set out [open $target w]\n" | |
| 1629 | +" set out [open $target wb]\n" | |
| 1611 | 1630 | " $in copyto $out\n" |
| 1612 | 1631 | " $out close\n" |
| 1613 | 1632 | " } on error {msg opts} {\n" |
| 1614 | 1633 | " incr opts(-level)\n" |
| 1615 | 1634 | " return {*}$opts $msg\n" |
| @@ -1691,11 +1710,11 @@ | ||
| 1691 | 1710 | " }\n" |
| 1692 | 1711 | " if {[llength $args] == 0} {\n" |
| 1693 | 1712 | " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n" |
| 1694 | 1713 | " }\n" |
| 1695 | 1714 | " 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" | |
| 1697 | 1716 | "\n" |
| 1698 | 1717 | " set handled 0\n" |
| 1699 | 1718 | "\n" |
| 1700 | 1719 | " foreach {on codes vars script} $args {\n" |
| 1701 | 1720 | " switch -- $on \\\n" |
| @@ -1709,16 +1728,16 @@ | ||
| 1709 | 1728 | " if {$optsvar ne \"\"} {\n" |
| 1710 | 1729 | " upvar $optsvar hopts\n" |
| 1711 | 1730 | " set hopts $opts\n" |
| 1712 | 1731 | " }\n" |
| 1713 | 1732 | "\n" |
| 1714 | -" set code [catch [list uplevel 1 $script] msg opts]\n" | |
| 1733 | +" set code [catch {uplevel 1 $script} msg opts]\n" | |
| 1715 | 1734 | " incr handled\n" |
| 1716 | 1735 | " }\n" |
| 1717 | 1736 | " } \\\n" |
| 1718 | 1737 | " finally {\n" |
| 1719 | -" set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts]\n" | |
| 1738 | +" set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n" | |
| 1720 | 1739 | " if {$finalcode} {\n" |
| 1721 | 1740 | "\n" |
| 1722 | 1741 | " set code $finalcode\n" |
| 1723 | 1742 | " set msg $finalmsg\n" |
| 1724 | 1743 | " set opts $finalopts\n" |
| @@ -2502,10 +2521,11 @@ | ||
| 2502 | 2521 | Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1))); |
| 2503 | 2522 | |
| 2504 | 2523 | return JIM_OK; |
| 2505 | 2524 | } |
| 2506 | 2525 | |
| 2526 | +#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)) | |
| 2507 | 2527 | static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, |
| 2508 | 2528 | const char *hdlfmt, int family, const char *mode[2]) |
| 2509 | 2529 | { |
| 2510 | 2530 | if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) { |
| 2511 | 2531 | Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); |
| @@ -2522,10 +2542,11 @@ | ||
| 2522 | 2542 | close(p[0]); |
| 2523 | 2543 | close(p[1]); |
| 2524 | 2544 | JimAioSetError(interp, NULL); |
| 2525 | 2545 | return JIM_ERR; |
| 2526 | 2546 | } |
| 2547 | +#endif | |
| 2527 | 2548 | |
| 2528 | 2549 | |
| 2529 | 2550 | int Jim_MakeTempFile(Jim_Interp *interp, const char *template) |
| 2530 | 2551 | { |
| 2531 | 2552 | #ifdef HAVE_MKSTEMP |
| @@ -2552,19 +2573,19 @@ | ||
| 2552 | 2573 | |
| 2553 | 2574 | |
| 2554 | 2575 | fd = mkstemp(filenameObj->bytes); |
| 2555 | 2576 | umask(mask); |
| 2556 | 2577 | if (fd < 0) { |
| 2557 | - Jim_SetResultString(interp, "Failed to create tempfile", -1); | |
| 2578 | + JimAioSetError(interp, filenameObj); | |
| 2558 | 2579 | Jim_FreeNewObj(interp, filenameObj); |
| 2559 | 2580 | return -1; |
| 2560 | 2581 | } |
| 2561 | 2582 | |
| 2562 | 2583 | Jim_SetResult(interp, filenameObj); |
| 2563 | 2584 | return fd; |
| 2564 | 2585 | #else |
| 2565 | - Jim_SetResultString(interp, "tempfile not supported", -1); | |
| 2586 | + Jim_SetResultString(interp, "platform has no tempfile support", -1); | |
| 2566 | 2587 | return -1; |
| 2567 | 2588 | #endif |
| 2568 | 2589 | } |
| 2569 | 2590 | |
| 2570 | 2591 | FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) |
| @@ -3176,10 +3197,16 @@ | ||
| 3176 | 3197 | |
| 3177 | 3198 | # ifndef MAXPATHLEN |
| 3178 | 3199 | # define MAXPATHLEN JIM_PATH_LEN |
| 3179 | 3200 | # endif |
| 3180 | 3201 | |
| 3202 | +#if defined(__MINGW32__) || defined(_MSC_VER) | |
| 3203 | +#define ISWINDOWS 1 | |
| 3204 | +#else | |
| 3205 | +#define ISWINDOWS 0 | |
| 3206 | +#endif | |
| 3207 | + | |
| 3181 | 3208 | |
| 3182 | 3209 | static const char *JimGetFileType(int mode) |
| 3183 | 3210 | { |
| 3184 | 3211 | if (S_ISREG(mode)) { |
| 3185 | 3212 | return "file"; |
| @@ -3281,16 +3308,14 @@ | ||
| 3281 | 3308 | Jim_SetResultString(interp, ".", -1); |
| 3282 | 3309 | } |
| 3283 | 3310 | else if (p == path) { |
| 3284 | 3311 | Jim_SetResultString(interp, "/", -1); |
| 3285 | 3312 | } |
| 3286 | -#if defined(__MINGW32__) || defined(_MSC_VER) | |
| 3287 | - else if (p[-1] == ':') { | |
| 3313 | + else if (ISWINDOWS && p[-1] == ':') { | |
| 3288 | 3314 | |
| 3289 | 3315 | Jim_SetResultString(interp, path, p - path + 1); |
| 3290 | 3316 | } |
| 3291 | -#endif | |
| 3292 | 3317 | else { |
| 3293 | 3318 | Jim_SetResultString(interp, path, p - path); |
| 3294 | 3319 | } |
| 3295 | 3320 | return JIM_OK; |
| 3296 | 3321 | } |
| @@ -3373,16 +3398,14 @@ | ||
| 3373 | 3398 | |
| 3374 | 3399 | if (*part == '/') { |
| 3375 | 3400 | |
| 3376 | 3401 | last = newname; |
| 3377 | 3402 | } |
| 3378 | -#if defined(__MINGW32__) || defined(_MSC_VER) | |
| 3379 | - else if (strchr(part, ':')) { | |
| 3403 | + else if (ISWINDOWS && strchr(part, ':')) { | |
| 3380 | 3404 | |
| 3381 | 3405 | last = newname; |
| 3382 | 3406 | } |
| 3383 | -#endif | |
| 3384 | 3407 | else if (part[0] == '.') { |
| 3385 | 3408 | if (part[1] == '/') { |
| 3386 | 3409 | part += 2; |
| 3387 | 3410 | len -= 2; |
| 3388 | 3411 | } |
| @@ -3407,11 +3430,14 @@ | ||
| 3407 | 3430 | last += len; |
| 3408 | 3431 | } |
| 3409 | 3432 | |
| 3410 | 3433 | |
| 3411 | 3434 | if (last > newname + 1 && last[-1] == '/') { |
| 3412 | - *--last = 0; | |
| 3435 | + | |
| 3436 | + if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) { | |
| 3437 | + *--last = 0; | |
| 3438 | + } | |
| 3413 | 3439 | } |
| 3414 | 3440 | } |
| 3415 | 3441 | |
| 3416 | 3442 | *last = 0; |
| 3417 | 3443 | |
| @@ -3591,10 +3617,48 @@ | ||
| 3591 | 3617 | return JIM_ERR; |
| 3592 | 3618 | } |
| 3593 | 3619 | |
| 3594 | 3620 | return JIM_OK; |
| 3595 | 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 | |
| 3596 | 3660 | |
| 3597 | 3661 | static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) |
| 3598 | 3662 | { |
| 3599 | 3663 | const char *path = Jim_String(filename); |
| 3600 | 3664 | |
| @@ -3889,10 +3953,19 @@ | ||
| 3889 | 3953 | file_cmd_rename, |
| 3890 | 3954 | 2, |
| 3891 | 3955 | 3, |
| 3892 | 3956 | |
| 3893 | 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 | |
| 3894 | 3967 | #if defined(HAVE_READLINK) |
| 3895 | 3968 | { "readlink", |
| 3896 | 3969 | "name", |
| 3897 | 3970 | file_cmd_readlink, |
| 3898 | 3971 | 1, |
| @@ -3982,19 +4055,17 @@ | ||
| 3982 | 4055 | if (getcwd(cwd, MAXPATHLEN) == NULL) { |
| 3983 | 4056 | Jim_SetResultString(interp, "Failed to get pwd", -1); |
| 3984 | 4057 | Jim_Free(cwd); |
| 3985 | 4058 | return JIM_ERR; |
| 3986 | 4059 | } |
| 3987 | -#if defined(__MINGW32__) || defined(_MSC_VER) | |
| 3988 | - { | |
| 4060 | + else if (ISWINDOWS) { | |
| 3989 | 4061 | |
| 3990 | 4062 | char *p = cwd; |
| 3991 | 4063 | while ((p = strchr(p, '\\')) != NULL) { |
| 3992 | 4064 | *p++ = '/'; |
| 3993 | 4065 | } |
| 3994 | 4066 | } |
| 3995 | -#endif | |
| 3996 | 4067 | |
| 3997 | 4068 | Jim_SetResultString(interp, cwd, -1); |
| 3998 | 4069 | |
| 3999 | 4070 | Jim_Free(cwd); |
| 4000 | 4071 | return JIM_OK; |
| @@ -5322,22 +5393,20 @@ | ||
| 5322 | 5393 | |
| 5323 | 5394 | static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len) |
| 5324 | 5395 | { |
| 5325 | 5396 | int fd = Jim_MakeTempFile(interp, NULL); |
| 5326 | 5397 | |
| 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 | + } | |
| 5339 | 5408 | } |
| 5340 | 5409 | return fd; |
| 5341 | 5410 | } |
| 5342 | 5411 | |
| 5343 | 5412 | static char **JimSaveEnv(char **env) |
| @@ -7768,10 +7837,11 @@ | ||
| 7768 | 7837 | |
| 7769 | 7838 | const char *Jim_String(Jim_Obj *objPtr) |
| 7770 | 7839 | { |
| 7771 | 7840 | if (objPtr->bytes == NULL) { |
| 7772 | 7841 | |
| 7842 | + JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value.")); | |
| 7773 | 7843 | JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); |
| 7774 | 7844 | objPtr->typePtr->updateStringProc(objPtr); |
| 7775 | 7845 | } |
| 7776 | 7846 | return objPtr->bytes; |
| 7777 | 7847 | } |
| @@ -8530,11 +8600,11 @@ | ||
| 8530 | 8600 | return objPtr; |
| 8531 | 8601 | } |
| 8532 | 8602 | |
| 8533 | 8603 | static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); |
| 8534 | 8604 | 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); | |
| 8536 | 8606 | static int JimParseCheckMissing(Jim_Interp *interp, int ch); |
| 8537 | 8607 | |
| 8538 | 8608 | static const Jim_ObjType scriptObjType = { |
| 8539 | 8609 | "script", |
| 8540 | 8610 | FreeScriptInternalRep, |
| @@ -8558,10 +8628,11 @@ | ||
| 8558 | 8628 | int inUse; /* Used to share a ScriptObj. Currently |
| 8559 | 8629 | only used by Jim_EvalObj() as protection against |
| 8560 | 8630 | shimmering of the currently evaluated object. */ |
| 8561 | 8631 | int firstline; |
| 8562 | 8632 | int linenr; |
| 8633 | + int missing; | |
| 8563 | 8634 | } ScriptObj; |
| 8564 | 8635 | |
| 8565 | 8636 | void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) |
| 8566 | 8637 | { |
| 8567 | 8638 | int i; |
| @@ -8836,19 +8907,18 @@ | ||
| 8836 | 8907 | } |
| 8837 | 8908 | |
| 8838 | 8909 | script->len = i; |
| 8839 | 8910 | } |
| 8840 | 8911 | |
| 8841 | -static int JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) | |
| 8912 | +static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) | |
| 8842 | 8913 | { |
| 8843 | 8914 | int scriptTextLen; |
| 8844 | 8915 | const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); |
| 8845 | 8916 | struct JimParserCtx parser; |
| 8846 | 8917 | struct ScriptObj *script; |
| 8847 | 8918 | ParseTokenList tokenlist; |
| 8848 | 8919 | int line = 1; |
| 8849 | - int retcode = JIM_OK; | |
| 8850 | 8920 | |
| 8851 | 8921 | |
| 8852 | 8922 | if (objPtr->typePtr == &sourceObjType) { |
| 8853 | 8923 | line = objPtr->internalRep.sourceValue.lineNumber; |
| 8854 | 8924 | } |
| @@ -8861,12 +8931,10 @@ | ||
| 8861 | 8931 | JimParseScript(&parser); |
| 8862 | 8932 | ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, |
| 8863 | 8933 | parser.tline); |
| 8864 | 8934 | } |
| 8865 | 8935 | |
| 8866 | - retcode = JimParseCheckMissing(interp, parser.missing.ch); | |
| 8867 | - | |
| 8868 | 8936 | |
| 8869 | 8937 | ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); |
| 8870 | 8938 | |
| 8871 | 8939 | |
| 8872 | 8940 | script = Jim_Alloc(sizeof(*script)); |
| @@ -8876,12 +8944,13 @@ | ||
| 8876 | 8944 | script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; |
| 8877 | 8945 | } |
| 8878 | 8946 | else { |
| 8879 | 8947 | script->fileNameObj = interp->emptyObj; |
| 8880 | 8948 | } |
| 8881 | - script->linenr = parser.missing.line; | |
| 8882 | 8949 | Jim_IncrRefCount(script->fileNameObj); |
| 8950 | + script->missing = parser.missing.ch; | |
| 8951 | + script->linenr = parser.missing.line; | |
| 8883 | 8952 | |
| 8884 | 8953 | ScriptObjAddTokens(interp, script, &tokenlist); |
| 8885 | 8954 | |
| 8886 | 8955 | |
| 8887 | 8956 | ScriptTokenListFree(&tokenlist); |
| @@ -8888,28 +8957,37 @@ | ||
| 8888 | 8957 | |
| 8889 | 8958 | |
| 8890 | 8959 | Jim_FreeIntRep(interp, objPtr); |
| 8891 | 8960 | Jim_SetIntRepPtr(objPtr, script); |
| 8892 | 8961 | objPtr->typePtr = &scriptObjType; |
| 8893 | - | |
| 8894 | - return retcode; | |
| 8895 | 8962 | } |
| 8896 | 8963 | |
| 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) | |
| 8898 | 8967 | { |
| 8899 | 8968 | if (objPtr == interp->emptyObj) { |
| 8900 | 8969 | |
| 8901 | 8970 | objPtr = interp->nullScriptObj; |
| 8902 | 8971 | } |
| 8903 | 8972 | |
| 8904 | 8973 | 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); | |
| 8908 | 8975 | } |
| 8909 | - return (ScriptObj *) Jim_GetIntRepPtr(objPtr); | |
| 8976 | + | |
| 8977 | + return (ScriptObj *)Jim_GetIntRepPtr(objPtr); | |
| 8910 | 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 | + | |
| 8911 | 8989 | |
| 8912 | 8990 | static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) |
| 8913 | 8991 | { |
| 8914 | 8992 | cmdPtr->inUse++; |
| 8915 | 8993 | } |
| @@ -10901,11 +10979,11 @@ | ||
| 10901 | 10979 | return JIM_OK; |
| 10902 | 10980 | } |
| 10903 | 10981 | else { |
| 10904 | 10982 | |
| 10905 | 10983 | 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); | |
| 10907 | 10985 | return JIM_ERR; |
| 10908 | 10986 | } |
| 10909 | 10987 | |
| 10910 | 10988 | Jim_FreeIntRep(interp, objPtr); |
| 10911 | 10989 | } |
| @@ -12331,10 +12409,11 @@ | ||
| 12331 | 12409 | JIM_EXPROP_UNARYPLUS, |
| 12332 | 12410 | |
| 12333 | 12411 | |
| 12334 | 12412 | JIM_EXPROP_FUNC_FIRST, |
| 12335 | 12413 | JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, |
| 12414 | + JIM_EXPROP_FUNC_WIDE, | |
| 12336 | 12415 | JIM_EXPROP_FUNC_ABS, |
| 12337 | 12416 | JIM_EXPROP_FUNC_DOUBLE, |
| 12338 | 12417 | JIM_EXPROP_FUNC_ROUND, |
| 12339 | 12418 | JIM_EXPROP_FUNC_RAND, |
| 12340 | 12419 | JIM_EXPROP_FUNC_SRAND, |
| @@ -12397,10 +12476,11 @@ | ||
| 12397 | 12476 | jim_wide wA, wC = 0; |
| 12398 | 12477 | |
| 12399 | 12478 | if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { |
| 12400 | 12479 | switch (e->opcode) { |
| 12401 | 12480 | case JIM_EXPROP_FUNC_INT: |
| 12481 | + case JIM_EXPROP_FUNC_WIDE: | |
| 12402 | 12482 | case JIM_EXPROP_FUNC_ROUND: |
| 12403 | 12483 | case JIM_EXPROP_UNARYPLUS: |
| 12404 | 12484 | wC = wA; |
| 12405 | 12485 | break; |
| 12406 | 12486 | case JIM_EXPROP_FUNC_DOUBLE: |
| @@ -12421,10 +12501,11 @@ | ||
| 12421 | 12501 | } |
| 12422 | 12502 | } |
| 12423 | 12503 | else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { |
| 12424 | 12504 | switch (e->opcode) { |
| 12425 | 12505 | case JIM_EXPROP_FUNC_INT: |
| 12506 | + case JIM_EXPROP_FUNC_WIDE: | |
| 12426 | 12507 | wC = dA; |
| 12427 | 12508 | break; |
| 12428 | 12509 | case JIM_EXPROP_FUNC_ROUND: |
| 12429 | 12510 | wC = dA < 0 ? (dA - 0.5) : (dA + 0.5); |
| 12430 | 12511 | break; |
| @@ -13093,10 +13174,11 @@ | ||
| 13093 | 13174 | OPRINIT(NULL, 150, 1, JimExprOpNumUnary), |
| 13094 | 13175 | |
| 13095 | 13176 | |
| 13096 | 13177 | |
| 13097 | 13178 | OPRINIT("int", 200, 1, JimExprOpNumUnary), |
| 13179 | + OPRINIT("wide", 200, 1, JimExprOpNumUnary), | |
| 13098 | 13180 | OPRINIT("abs", 200, 1, JimExprOpNumUnary), |
| 13099 | 13181 | OPRINIT("double", 200, 1, JimExprOpNumUnary), |
| 13100 | 13182 | OPRINIT("round", 200, 1, JimExprOpNumUnary), |
| 13101 | 13183 | OPRINIT("rand", 200, 0, JimExprOpNone), |
| 13102 | 13184 | OPRINIT("srand", 200, 1, JimExprOpIntUnary), |
| @@ -14750,15 +14832,13 @@ | ||
| 14750 | 14832 | ret = Jim_EvalObjVector(interp, objc + 1, nargv); |
| 14751 | 14833 | Jim_Free(nargv); |
| 14752 | 14834 | return ret; |
| 14753 | 14835 | } |
| 14754 | 14836 | |
| 14755 | -static void JimAddErrorToStack(Jim_Interp *interp, int retcode, ScriptObj *script) | |
| 14837 | +static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script) | |
| 14756 | 14838 | { |
| 14757 | - int rc = retcode; | |
| 14758 | - | |
| 14759 | - if (rc == JIM_ERR && !interp->errorFlag) { | |
| 14839 | + if (!interp->errorFlag) { | |
| 14760 | 14840 | |
| 14761 | 14841 | interp->errorFlag = 1; |
| 14762 | 14842 | Jim_IncrRefCount(script->fileNameObj); |
| 14763 | 14843 | Jim_DecrRefCount(interp, interp->errorFileNameObj); |
| 14764 | 14844 | interp->errorFileNameObj = script->fileNameObj; |
| @@ -14768,11 +14848,11 @@ | ||
| 14768 | 14848 | |
| 14769 | 14849 | interp->addStackTrace++; |
| 14770 | 14850 | } |
| 14771 | 14851 | |
| 14772 | 14852 | |
| 14773 | - if (rc == JIM_ERR && interp->addStackTrace > 0) { | |
| 14853 | + if (interp->addStackTrace > 0) { | |
| 14774 | 14854 | |
| 14775 | 14855 | |
| 14776 | 14856 | JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr); |
| 14777 | 14857 | |
| 14778 | 14858 | if (Jim_Length(script->fileNameObj)) { |
| @@ -14781,16 +14861,10 @@ | ||
| 14781 | 14861 | |
| 14782 | 14862 | Jim_DecrRefCount(interp, interp->errorProc); |
| 14783 | 14863 | interp->errorProc = interp->emptyObj; |
| 14784 | 14864 | Jim_IncrRefCount(interp->errorProc); |
| 14785 | 14865 | } |
| 14786 | - else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) { | |
| 14787 | - | |
| 14788 | - } | |
| 14789 | - else { | |
| 14790 | - interp->addStackTrace = 0; | |
| 14791 | - } | |
| 14792 | 14866 | } |
| 14793 | 14867 | |
| 14794 | 14868 | static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) |
| 14795 | 14869 | { |
| 14796 | 14870 | Jim_Obj *objPtr; |
| @@ -14928,10 +15002,12 @@ | ||
| 14928 | 15002 | |
| 14929 | 15003 | static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) |
| 14930 | 15004 | { |
| 14931 | 15005 | int retcode = JIM_OK; |
| 14932 | 15006 | |
| 15007 | + JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list.")); | |
| 15008 | + | |
| 14933 | 15009 | if (listPtr->internalRep.listValue.len) { |
| 14934 | 15010 | Jim_IncrRefCount(listPtr); |
| 14935 | 15011 | retcode = JimInvokeCommand(interp, |
| 14936 | 15012 | listPtr->internalRep.listValue.len, |
| 14937 | 15013 | listPtr->internalRep.listValue.ele); |
| @@ -14958,12 +15034,12 @@ | ||
| 14958 | 15034 | if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { |
| 14959 | 15035 | return JimEvalObjList(interp, scriptObjPtr); |
| 14960 | 15036 | } |
| 14961 | 15037 | |
| 14962 | 15038 | Jim_IncrRefCount(scriptObjPtr); |
| 14963 | - script = Jim_GetScript(interp, scriptObjPtr); | |
| 14964 | - if (script == NULL) { | |
| 15039 | + script = JimGetScript(interp, scriptObjPtr); | |
| 15040 | + if (!JimScriptValid(interp, script)) { | |
| 14965 | 15041 | Jim_DecrRefCount(interp, scriptObjPtr); |
| 14966 | 15042 | return JIM_ERR; |
| 14967 | 15043 | } |
| 14968 | 15044 | |
| 14969 | 15045 | Jim_SetEmptyResult(interp); |
| @@ -15125,11 +15201,18 @@ | ||
| 15125 | 15201 | argv = sargv; |
| 15126 | 15202 | } |
| 15127 | 15203 | } |
| 15128 | 15204 | |
| 15129 | 15205 | |
| 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 | + } | |
| 15131 | 15214 | |
| 15132 | 15215 | |
| 15133 | 15216 | interp->currentScriptObj = prevScriptObj; |
| 15134 | 15217 | |
| 15135 | 15218 | Jim_FreeIntRep(interp, scriptObjPtr); |
| @@ -15276,11 +15359,11 @@ | ||
| 15276 | 15359 | callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; |
| 15277 | 15360 | callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; |
| 15278 | 15361 | callFramePtr->staticVars = cmd->u.proc.staticVars; |
| 15279 | 15362 | |
| 15280 | 15363 | |
| 15281 | - script = Jim_GetScript(interp, interp->currentScriptObj); | |
| 15364 | + script = JimGetScript(interp, interp->currentScriptObj); | |
| 15282 | 15365 | callFramePtr->fileNameObj = script->fileNameObj; |
| 15283 | 15366 | callFramePtr->line = script->linenr; |
| 15284 | 15367 | |
| 15285 | 15368 | Jim_IncrRefCount(cmd->u.proc.argListObjPtr); |
| 15286 | 15369 | Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); |
| @@ -15473,18 +15556,10 @@ | ||
| 15473 | 15556 | |
| 15474 | 15557 | scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); |
| 15475 | 15558 | JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1); |
| 15476 | 15559 | Jim_IncrRefCount(scriptObjPtr); |
| 15477 | 15560 | |
| 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 | 15561 | prevScriptObj = interp->currentScriptObj; |
| 15487 | 15562 | interp->currentScriptObj = scriptObjPtr; |
| 15488 | 15563 | |
| 15489 | 15564 | retcode = Jim_EvalObj(interp, scriptObjPtr); |
| 15490 | 15565 | |
| @@ -16030,11 +16105,11 @@ | ||
| 16030 | 16105 | Jim_Obj *objPtr; |
| 16031 | 16106 | int cmpOffset; |
| 16032 | 16107 | |
| 16033 | 16108 | |
| 16034 | 16109 | expr = JimGetExpression(interp, argv[2]); |
| 16035 | - incrScript = Jim_GetScript(interp, argv[3]); | |
| 16110 | + incrScript = JimGetScript(interp, argv[3]); | |
| 16036 | 16111 | |
| 16037 | 16112 | |
| 16038 | 16113 | if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) { |
| 16039 | 16114 | goto evalstart; |
| 16040 | 16115 | } |
| @@ -17017,11 +17092,11 @@ | ||
| 17017 | 17092 | { |
| 17018 | 17093 | Jim_Obj *stringObjPtr; |
| 17019 | 17094 | int i; |
| 17020 | 17095 | |
| 17021 | 17096 | if (argc < 2) { |
| 17022 | - Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); | |
| 17097 | + Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?"); | |
| 17023 | 17098 | return JIM_ERR; |
| 17024 | 17099 | } |
| 17025 | 17100 | if (argc == 2) { |
| 17026 | 17101 | stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); |
| 17027 | 17102 | if (!stringObjPtr) |
| @@ -17066,11 +17141,11 @@ | ||
| 17066 | 17141 | static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
| 17067 | 17142 | { |
| 17068 | 17143 | int rc; |
| 17069 | 17144 | |
| 17070 | 17145 | if (argc < 2) { |
| 17071 | - Jim_WrongNumArgs(interp, 1, argv, "script ?...?"); | |
| 17146 | + Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); | |
| 17072 | 17147 | return JIM_ERR; |
| 17073 | 17148 | } |
| 17074 | 17149 | |
| 17075 | 17150 | if (argc == 2) { |
| 17076 | 17151 | rc = Jim_EvalObj(interp, argv[1]); |
| @@ -17277,11 +17352,11 @@ | ||
| 17277 | 17352 | Jim_Obj *cmdList; |
| 17278 | 17353 | Jim_Obj *prefixListObj = Jim_CmdPrivData(interp); |
| 17279 | 17354 | |
| 17280 | 17355 | |
| 17281 | 17356 | 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); | |
| 17283 | 17358 | |
| 17284 | 17359 | return JimEvalObjList(interp, cmdList); |
| 17285 | 17360 | } |
| 17286 | 17361 | |
| 17287 | 17362 | static void JimAliasCmdDelete(Jim_Interp *interp, void *privData) |
| @@ -17597,17 +17672,17 @@ | ||
| 17597 | 17672 | int len; |
| 17598 | 17673 | int opt_case = 1; |
| 17599 | 17674 | int option; |
| 17600 | 17675 | static const char * const options[] = { |
| 17601 | 17676 | "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace", |
| 17602 | - "map", "repeat", "reverse", "index", "first", "last", | |
| 17677 | + "map", "repeat", "reverse", "index", "first", "last", "cat", | |
| 17603 | 17678 | "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL |
| 17604 | 17679 | }; |
| 17605 | 17680 | enum |
| 17606 | 17681 | { |
| 17607 | 17682 | 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, | |
| 17609 | 17684 | OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE |
| 17610 | 17685 | }; |
| 17611 | 17686 | static const char * const nocase_options[] = { |
| 17612 | 17687 | "-nocase", NULL |
| 17613 | 17688 | }; |
| @@ -17636,10 +17711,29 @@ | ||
| 17636 | 17711 | else { |
| 17637 | 17712 | len = Jim_Length(argv[2]); |
| 17638 | 17713 | } |
| 17639 | 17714 | Jim_SetResultInt(interp, len); |
| 17640 | 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 | + } | |
| 17641 | 17735 | |
| 17642 | 17736 | case OPT_COMPARE: |
| 17643 | 17737 | case OPT_EQUAL: |
| 17644 | 17738 | { |
| 17645 | 17739 | |
| @@ -18619,38 +18713,47 @@ | ||
| 18619 | 18713 | case INFO_SCRIPT: |
| 18620 | 18714 | if (argc != 2) { |
| 18621 | 18715 | Jim_WrongNumArgs(interp, 2, argv, ""); |
| 18622 | 18716 | return JIM_ERR; |
| 18623 | 18717 | } |
| 18624 | - Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj); | |
| 18718 | + Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj); | |
| 18625 | 18719 | break; |
| 18626 | 18720 | |
| 18627 | 18721 | case INFO_SOURCE:{ |
| 18628 | - int line; | |
| 18722 | + jim_wide line; | |
| 18629 | 18723 | Jim_Obj *resObjPtr; |
| 18630 | 18724 | Jim_Obj *fileNameObj; |
| 18631 | 18725 | |
| 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?"); | |
| 18634 | 18728 | return JIM_ERR; |
| 18635 | 18729 | } |
| 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); | |
| 18644 | 18736 | } |
| 18645 | 18737 | 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)); | |
| 18648 | 18754 | } |
| 18649 | - resObjPtr = Jim_NewListObj(interp, NULL, 0); | |
| 18650 | - Jim_ListAppendElement(interp, resObjPtr, fileNameObj); | |
| 18651 | - Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); | |
| 18652 | 18755 | Jim_SetResult(interp, resObjPtr); |
| 18653 | 18756 | break; |
| 18654 | 18757 | } |
| 18655 | 18758 | |
| 18656 | 18759 | case INFO_STACKTRACE: |
| @@ -21843,11 +21946,11 @@ | ||
| 21843 | 21946 | |
| 21844 | 21947 | if (Jim_InitStaticExtensions(interp) != JIM_OK) { |
| 21845 | 21948 | JimPrintErrorMessage(interp); |
| 21846 | 21949 | } |
| 21847 | 21950 | |
| 21848 | - Jim_SetVariableStrWithStr(interp, "jim_argv0", argv[0]); | |
| 21951 | + Jim_SetVariableStrWithStr(interp, "jim::argv0", argv[0]); | |
| 21849 | 21952 | Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); |
| 21850 | 21953 | retcode = Jim_initjimshInit(interp); |
| 21851 | 21954 | |
| 21852 | 21955 | if (argc == 1) { |
| 21853 | 21956 | if (retcode == JIM_ERR) { |
| 21854 | 21957 |
| --- 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 |
+1
-1
| --- skins/black_and_white/header.txt | ||
| +++ skins/black_and_white/header.txt | ||
| @@ -34,11 +34,11 @@ | ||
| 34 | 34 | if {[hascap o]} { |
| 35 | 35 | html "<a href='$home/brlist'>Branches</a>\n" |
| 36 | 36 | html "<a href='$home/taglist'>Tags</a>\n" |
| 37 | 37 | } |
| 38 | 38 | if {[hascap r]} { |
| 39 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 39 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 40 | 40 | } |
| 41 | 41 | if {[hascap j]} { |
| 42 | 42 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 43 | 43 | } |
| 44 | 44 | if {[hascap s]} { |
| 45 | 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/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 |
+1
-1
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -33,11 +33,11 @@ | ||
| 33 | 33 | if {[hascap o]} { |
| 34 | 34 | html "<a href='$home/brlist'>Branches</a>\n" |
| 35 | 35 | html "<a href='$home/taglist'>Tags</a>\n" |
| 36 | 36 | } |
| 37 | 37 | if {[hascap r]} { |
| 38 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 38 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 39 | 39 | } |
| 40 | 40 | if {[hascap j]} { |
| 41 | 41 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 42 | 42 | } |
| 43 | 43 | if {[hascap s]} { |
| 44 | 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/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 |
+1
-1
| --- skins/eagle/css.txt | ||
| +++ skins/eagle/css.txt | ||
| @@ -187,11 +187,11 @@ | ||
| 187 | 187 | |
| 188 | 188 | /* format for the user list table on the user setup page */ |
| 189 | 189 | table.usetupUserList { |
| 190 | 190 | outline-style: double; |
| 191 | 191 | outline-width: 1px; |
| 192 | - border-color: white; | |
| 192 | + outline-color: white; | |
| 193 | 193 | padding: 10px; |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | 196 | /* color for capabilities, inherited by reader */ |
| 197 | 197 | span.ueditInheritReader { |
| 198 | 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 | 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 |
+1
-1
| --- skins/eagle/header.txt | ||
| +++ skins/eagle/header.txt | ||
| @@ -114,11 +114,11 @@ | ||
| 114 | 114 | if {[hascap o]} { |
| 115 | 115 | html "<a href='$home/brlist'>Branches</a>\n" |
| 116 | 116 | html "<a href='$home/taglist'>Tags</a>\n" |
| 117 | 117 | } |
| 118 | 118 | if {[hascap r]} { |
| 119 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 119 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 120 | 120 | } |
| 121 | 121 | if {[hascap j]} { |
| 122 | 122 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 123 | 123 | } |
| 124 | 124 | if {[hascap s]} { |
| 125 | 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/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 |
+1
-1
| --- skins/enhanced1/header.txt | ||
| +++ skins/enhanced1/header.txt | ||
| @@ -114,11 +114,11 @@ | ||
| 114 | 114 | if {[hascap o]} { |
| 115 | 115 | html "<a href='$home/brlist'>Branches</a>\n" |
| 116 | 116 | html "<a href='$home/taglist'>Tags</a>\n" |
| 117 | 117 | } |
| 118 | 118 | if {[hascap r]} { |
| 119 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 119 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 120 | 120 | } |
| 121 | 121 | if {[hascap j]} { |
| 122 | 122 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 123 | 123 | } |
| 124 | 124 | if {[hascap s]} { |
| 125 | 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/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 |
+2
-2
| --- skins/etienne1/header.txt | ||
| +++ skins/etienne1/header.txt | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | || ($pagename eq "home" && $comphome == 0) |
| 52 | 52 | || ($pagename eq "setup" && $compsetup == 0) |
| 53 | 53 | || ($pagename eq "taglist" && $comptag == 0) |
| 54 | 54 | || ($pagename eq "dir" && $compdir == 0) |
| 55 | 55 | || ($pagename eq "timeline" && $comptl == 0) |
| 56 | - || ($pagename eq "reportlist" && $comptkt == 0) | |
| 56 | + || ($pagename eq "ticket" && $comptkt == 0) | |
| 57 | 57 | || ($pagename eq "brlist" && $compbr == 0) |
| 58 | 58 | } { |
| 59 | 59 | html " class='active' " |
| 60 | 60 | } |
| 61 | 61 | |
| @@ -73,11 +73,11 @@ | ||
| 73 | 73 | if {[hascap o]} { |
| 74 | 74 | menulink "brlist" "/brlist" Branches |
| 75 | 75 | menulink "taglist" "/taglist" Tags |
| 76 | 76 | } |
| 77 | 77 | if {[hascap r]} { |
| 78 | - menulink "reportlist" "/reportlist" Tickets | |
| 78 | + menulink "ticket" "/ticket" Tickets | |
| 79 | 79 | } |
| 80 | 80 | if {[hascap j]} { |
| 81 | 81 | menulink "wiki" "/wiki" Wiki |
| 82 | 82 | } |
| 83 | 83 | if {[hascap s]} { |
| 84 | 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 "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 |
+1
-1
| --- skins/khaki/header.txt | ||
| +++ skins/khaki/header.txt | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | if {[hascap o]} { |
| 33 | 33 | html "<a href='$home/brlist'>Branches</a>\n" |
| 34 | 34 | html "<a href='$home/taglist'>Tags</a>\n" |
| 35 | 35 | } |
| 36 | 36 | if {[hascap r]} { |
| 37 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 37 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 38 | 38 | } |
| 39 | 39 | if {[hascap j]} { |
| 40 | 40 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 41 | 41 | } |
| 42 | 42 | if {[hascap s]} { |
| 43 | 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/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 |
+1
-1
| --- skins/plain_gray/header.txt | ||
| +++ skins/plain_gray/header.txt | ||
| @@ -30,11 +30,11 @@ | ||
| 30 | 30 | if {[hascap o]} { |
| 31 | 31 | html "<a href='$home/brlist'>Branches</a>\n" |
| 32 | 32 | html "<a href='$home/taglist'>Tags</a>\n" |
| 33 | 33 | } |
| 34 | 34 | if {[hascap r]} { |
| 35 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 35 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 36 | 36 | } |
| 37 | 37 | if {[hascap j]} { |
| 38 | 38 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 39 | 39 | } |
| 40 | 40 | if {[hascap s]} { |
| 41 | 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/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 |
+1
-1
| --- skins/rounded1/header.txt | ||
| +++ skins/rounded1/header.txt | ||
| @@ -34,11 +34,11 @@ | ||
| 34 | 34 | if {[hascap o]} { |
| 35 | 35 | html "<a href='$home/brlist'>Branches</a>\n" |
| 36 | 36 | html "<a href='$home/taglist'>Tags</a>\n" |
| 37 | 37 | } |
| 38 | 38 | if {[hascap r]} { |
| 39 | - html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 39 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 40 | 40 | } |
| 41 | 41 | if {[hascap j]} { |
| 42 | 42 | html "<a href='$home/wiki'>Wiki</a>\n" |
| 43 | 43 | } |
| 44 | 44 | if {[hascap s]} { |
| 45 | 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/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 |
+5
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -229,15 +229,20 @@ | ||
| 229 | 229 | collect_argument(&extra, "vacuum",0); |
| 230 | 230 | collect_argument(&extra, "deanalyze",0); |
| 231 | 231 | collect_argument(&extra, "analyze",0); |
| 232 | 232 | collect_argument(&extra, "wal",0); |
| 233 | 233 | collect_argument(&extra, "stats",0); |
| 234 | + collect_argument(&extra, "index",0); | |
| 235 | + collect_argument(&extra, "no-index",0); | |
| 234 | 236 | }else if( strncmp(zCmd, "setting", n)==0 ){ |
| 235 | 237 | zCmd = "setting -R"; |
| 236 | 238 | collect_argv(&extra, 3); |
| 237 | 239 | }else if( strncmp(zCmd, "unset", n)==0 ){ |
| 238 | 240 | zCmd = "unset -R"; |
| 241 | + collect_argv(&extra, 3); | |
| 242 | + }else if( strncmp(zCmd, "fts-config", n)==0 ){ | |
| 243 | + zCmd = "fts-config -R"; | |
| 239 | 244 | collect_argv(&extra, 3); |
| 240 | 245 | }else if( strncmp(zCmd, "sync", n)==0 ){ |
| 241 | 246 | zCmd = "sync -autourl -R"; |
| 242 | 247 | collect_argument(&extra, "verbose","v"); |
| 243 | 248 | }else if( strncmp(zCmd, "test-integrity", n)==0 ){ |
| 244 | 249 |
| --- 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 @@ | ||
| 138 | 138 | |
| 139 | 139 | if( g.argc < 4 ){ |
| 140 | 140 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 141 | 141 | } |
| 142 | 142 | db_open_config(0); |
| 143 | - if( file_size(g.argv[3])>0 ){ | |
| 143 | + if( -1 != file_size(g.argv[3]) ){ | |
| 144 | 144 | fossil_fatal("file already exists: %s", g.argv[3]); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | 147 | url_parse(g.argv[2], urlFlags); |
| 148 | 148 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| 149 | 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( 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 @@ | ||
| 90 | 90 | { "footer", CONFIGSET_SKIN }, |
| 91 | 91 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 92 | 92 | { "logo-image", CONFIGSET_SKIN }, |
| 93 | 93 | { "background-mimetype", CONFIGSET_SKIN }, |
| 94 | 94 | { "background-image", CONFIGSET_SKIN }, |
| 95 | - { "index-page", CONFIGSET_SKIN }, | |
| 96 | 95 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 97 | 96 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 98 | 97 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | 98 | { "adunit", CONFIGSET_SKIN }, |
| 100 | 99 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| @@ -115,10 +114,11 @@ | ||
| 115 | 114 | #endif |
| 116 | 115 | |
| 117 | 116 | { "project-name", CONFIGSET_PROJ }, |
| 118 | 117 | { "short-project-name", CONFIGSET_PROJ }, |
| 119 | 118 | { "project-description", CONFIGSET_PROJ }, |
| 119 | + { "index-page", CONFIGSET_PROJ }, | |
| 120 | 120 | { "manifest", CONFIGSET_PROJ }, |
| 121 | 121 | { "binary-glob", CONFIGSET_PROJ }, |
| 122 | 122 | { "clean-glob", CONFIGSET_PROJ }, |
| 123 | 123 | { "ignore-glob", CONFIGSET_PROJ }, |
| 124 | 124 | { "keep-glob", CONFIGSET_PROJ }, |
| @@ -1061,11 +1061,11 @@ | ||
| 1061 | 1061 | zTrans[j++] = 'n'; |
| 1062 | 1062 | }else if( c=='\r' ){ |
| 1063 | 1063 | zTrans[j++] = 'r'; |
| 1064 | 1064 | }else if( c=='\t' ){ |
| 1065 | 1065 | zTrans[j++] = 't'; |
| 1066 | - }else{ | |
| 1066 | + }else{ | |
| 1067 | 1067 | zTrans[j++] = '0' + ((c>>6)&7); |
| 1068 | 1068 | zTrans[j++] = '0' + ((c>>3)&7); |
| 1069 | 1069 | zTrans[j++] = '0' + (c&7); |
| 1070 | 1070 | } |
| 1071 | 1071 | } |
| @@ -1098,11 +1098,11 @@ | ||
| 1098 | 1098 | /* |
| 1099 | 1099 | ** COMMAND: test-var-get |
| 1100 | 1100 | ** |
| 1101 | 1101 | ** Usage: %fossil test-var-get VAR ?FILE? |
| 1102 | 1102 | ** |
| 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 "-" | |
| 1104 | 1104 | ** or is omitted then output goes to standard output. VAR can be a |
| 1105 | 1105 | ** GLOB pattern. |
| 1106 | 1106 | ** |
| 1107 | 1107 | ** If not in an open check-out, use the -R REPO option to specify a |
| 1108 | 1108 | ** a repository. |
| 1109 | 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 | { "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 |
M
src/db.c
+139
-88
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -547,12 +547,13 @@ | ||
| 547 | 547 | va_end(ap); |
| 548 | 548 | z = blob_str(&sql); |
| 549 | 549 | while( rc==SQLITE_OK && z[0] ){ |
| 550 | 550 | pStmt = 0; |
| 551 | 551 | 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 ){ | |
| 554 | 555 | db.nPrepare++; |
| 555 | 556 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 556 | 557 | rc = sqlite3_finalize(pStmt); |
| 557 | 558 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 558 | 559 | } |
| @@ -1197,11 +1198,11 @@ | ||
| 1197 | 1198 | */ |
| 1198 | 1199 | if( !db_table_has_column("repository","mlink","isaux") ){ |
| 1199 | 1200 | db_begin_transaction(); |
| 1200 | 1201 | db_multi_exec( |
| 1201 | 1202 | "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;", | |
| 1203 | 1204 | db_name("repository"), db_name("repository") |
| 1204 | 1205 | ); |
| 1205 | 1206 | db_end_transaction(0); |
| 1206 | 1207 | } |
| 1207 | 1208 | } |
| @@ -1380,15 +1381,10 @@ | ||
| 1380 | 1381 | while( db.pAllStmt ){ |
| 1381 | 1382 | db_finalize(db.pAllStmt); |
| 1382 | 1383 | } |
| 1383 | 1384 | db_end_transaction(1); |
| 1384 | 1385 | 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 | 1386 | db_close_config(); |
| 1391 | 1387 | |
| 1392 | 1388 | /* If the localdb (the check-out database) is open and if it has |
| 1393 | 1389 | ** a lot of unused free space, then VACUUM it as we shut down. |
| 1394 | 1390 | */ |
| @@ -1399,12 +1395,18 @@ | ||
| 1399 | 1395 | db_multi_exec("VACUUM;"); |
| 1400 | 1396 | } |
| 1401 | 1397 | } |
| 1402 | 1398 | |
| 1403 | 1399 | if( g.db ){ |
| 1400 | + int rc; | |
| 1404 | 1401 | 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 | + } | |
| 1406 | 1408 | g.db = 0; |
| 1407 | 1409 | g.zMainDbType = 0; |
| 1408 | 1410 | } |
| 1409 | 1411 | g.repositoryOpen = 0; |
| 1410 | 1412 | g.localOpen = 0; |
| @@ -1520,12 +1522,12 @@ | ||
| 1520 | 1522 | int i; |
| 1521 | 1523 | const char *zSep = ""; |
| 1522 | 1524 | |
| 1523 | 1525 | blob_zero(&x); |
| 1524 | 1526 | 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); | |
| 1527 | 1529 | zSep = ","; |
| 1528 | 1530 | } |
| 1529 | 1531 | blob_append_sql(&x, ")"); |
| 1530 | 1532 | return blob_sql_text(&x); |
| 1531 | 1533 | } |
| @@ -1682,10 +1684,15 @@ | ||
| 1682 | 1684 | verify_all_options(); |
| 1683 | 1685 | |
| 1684 | 1686 | if( g.argc!=3 ){ |
| 1685 | 1687 | usage("REPOSITORY-NAME"); |
| 1686 | 1688 | } |
| 1689 | + | |
| 1690 | + if( -1 != file_size(g.argv[2]) ){ | |
| 1691 | + fossil_fatal("file already exists: %s", g.argv[2]); | |
| 1692 | + } | |
| 1693 | + | |
| 1687 | 1694 | db_create_repository(g.argv[2]); |
| 1688 | 1695 | db_open_repository(g.argv[2]); |
| 1689 | 1696 | db_open_config(0); |
| 1690 | 1697 | if( zTemplate ) db_attach(zTemplate, "settingSrc"); |
| 1691 | 1698 | db_begin_transaction(); |
| @@ -1913,16 +1920,21 @@ | ||
| 1913 | 1920 | g.zConfigDbType = zTempDbType; |
| 1914 | 1921 | } |
| 1915 | 1922 | } |
| 1916 | 1923 | |
| 1917 | 1924 | /* |
| 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. | |
| 1922 | 1934 | */ |
| 1923 | -char *db_get_do_versionable(const char *zName, char *zNonVersionedSetting){ | |
| 1935 | +char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ | |
| 1924 | 1936 | char *zVersionedSetting = 0; |
| 1925 | 1937 | int noWarn = 0; |
| 1926 | 1938 | struct _cacheEntry { |
| 1927 | 1939 | struct _cacheEntry *next; |
| 1928 | 1940 | const char *zName, *zValue; |
| @@ -1993,37 +2005,38 @@ | ||
| 1993 | 2005 | |
| 1994 | 2006 | |
| 1995 | 2007 | /* |
| 1996 | 2008 | ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the |
| 1997 | 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. | |
| 1998 | 2015 | */ |
| 1999 | 2016 | char *db_get(const char *zName, char *zDefault){ |
| 2000 | 2017 | 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); | |
| 2010 | 2019 | if( g.repositoryOpen ){ |
| 2011 | 2020 | z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName); |
| 2012 | 2021 | } |
| 2013 | 2022 | if( z==0 && g.zConfigDbName ){ |
| 2014 | 2023 | db_swap_connections(); |
| 2015 | 2024 | z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName); |
| 2016 | 2025 | db_swap_connections(); |
| 2017 | 2026 | } |
| 2018 | - if( ctrlSetting!=0 && ctrlSetting->versionable ){ | |
| 2027 | + if( pSetting!=0 && pSetting->versionable ){ | |
| 2019 | 2028 | /* This is a versionable setting, try and get the info from a |
| 2020 | 2029 | ** checked out file */ |
| 2021 | - z = db_get_do_versionable(zName, z); | |
| 2030 | + z = db_get_versioned(zName, z); | |
| 2022 | 2031 | } |
| 2023 | 2032 | 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 | + } | |
| 2025 | 2038 | } |
| 2026 | 2039 | return z; |
| 2027 | 2040 | } |
| 2028 | 2041 | char *db_get_mtime(const char *zName, char *zFormat, char *zDefault){ |
| 2029 | 2042 | char *z = 0; |
| @@ -2285,88 +2298,91 @@ | ||
| 2285 | 2298 | g.argc = 2; |
| 2286 | 2299 | info_cmd(); |
| 2287 | 2300 | } |
| 2288 | 2301 | |
| 2289 | 2302 | /* |
| 2290 | -** Print the value of a setting named zName | |
| 2303 | +** Print the current value of a setting identified by the pSetting | |
| 2304 | +** pointer. | |
| 2291 | 2305 | */ |
| 2292 | -static void print_setting( | |
| 2293 | - const struct stControlSettings *ctrlSetting, | |
| 2294 | - int localOpen | |
| 2295 | -){ | |
| 2306 | +static void print_setting(const Setting *pSetting){ | |
| 2296 | 2307 | Stmt q; |
| 2297 | 2308 | if( g.repositoryOpen ){ |
| 2298 | 2309 | db_prepare(&q, |
| 2299 | 2310 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| 2300 | 2311 | " UNION ALL " |
| 2301 | 2312 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 2302 | - ctrlSetting->name, ctrlSetting->name | |
| 2313 | + pSetting->name, pSetting->name | |
| 2303 | 2314 | ); |
| 2304 | 2315 | }else{ |
| 2305 | 2316 | db_prepare(&q, |
| 2306 | 2317 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 2307 | - ctrlSetting->name | |
| 2318 | + pSetting->name | |
| 2308 | 2319 | ); |
| 2309 | 2320 | } |
| 2310 | 2321 | 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), | |
| 2312 | 2323 | db_column_text(&q, 1)); |
| 2313 | 2324 | }else{ |
| 2314 | - fossil_print("%-20s\n", ctrlSetting->name); | |
| 2325 | + fossil_print("%-20s\n", pSetting->name); | |
| 2315 | 2326 | } |
| 2316 | - if( ctrlSetting->versionable && localOpen ){ | |
| 2327 | + if( pSetting->versionable && g.localOpen ){ | |
| 2317 | 2328 | /* Check to see if this is overridden by a versionable settings file */ |
| 2318 | 2329 | Blob versionedPathname; |
| 2319 | 2330 | blob_zero(&versionedPathname); |
| 2320 | 2331 | blob_appendf(&versionedPathname, "%s/.fossil-settings/%s", |
| 2321 | - g.zLocalRoot, ctrlSetting->name); | |
| 2332 | + g.zLocalRoot, pSetting->name); | |
| 2322 | 2333 | if( file_size(blob_str(&versionedPathname))>=0 ){ |
| 2323 | 2334 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 2324 | - ctrlSetting->name); | |
| 2335 | + pSetting->name); | |
| 2325 | 2336 | } |
| 2326 | 2337 | } |
| 2327 | 2338 | db_finalize(&q); |
| 2328 | 2339 | } |
| 2329 | 2340 | |
| 2330 | 2341 | |
| 2342 | +#if INTERFACE | |
| 2331 | 2343 | /* |
| 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. | |
| 2334 | 2348 | ** If var is 0, the settings name is used. |
| 2349 | +** | |
| 2335 | 2350 | ** width is the length for the edit field on the behavior page, 0 |
| 2336 | 2351 | ** is used for on/off checkboxes. |
| 2352 | +** | |
| 2337 | 2353 | ** The behaviour page doesn't use a special layout. It lists all |
| 2338 | 2354 | ** set-commands and displays the 'set'-help as info. |
| 2339 | 2355 | */ |
| 2340 | -#if INTERFACE | |
| 2341 | -struct stControlSettings { | |
| 2356 | +struct Setting { | |
| 2342 | 2357 | const char *name; /* Name of the setting */ |
| 2343 | 2358 | const char *var; /* Internal variable name used by db_set() */ |
| 2344 | 2359 | int width; /* Width of display. 0 for boolean values. */ |
| 2345 | 2360 | int versionable; /* Is this setting versionable? */ |
| 2346 | 2361 | int forceTextArea; /* Force using a text area for display? */ |
| 2347 | 2362 | const char *def; /* Default value */ |
| 2348 | 2363 | }; |
| 2349 | 2364 | #endif /* INTERFACE */ |
| 2350 | -struct stControlSettings const ctrlSettings[] = { | |
| 2365 | + | |
| 2366 | +const Setting aSetting[] = { | |
| 2351 | 2367 | { "access-log", 0, 0, 0, 0, "off" }, |
| 2352 | 2368 | { "admin-log", 0, 0, 0, 0, "off" }, |
| 2353 | 2369 | { "allow-symlinks", 0, 0, 1, 0, "off" }, |
| 2354 | 2370 | { "auto-captcha", "autocaptcha", 0, 0, 0, "on" }, |
| 2355 | 2371 | { "auto-hyperlink", 0, 0, 0, 0, "on", }, |
| 2356 | 2372 | { "auto-shun", 0, 0, 0, 0, "on" }, |
| 2357 | 2373 | { "autosync", 0, 0, 0, 0, "on" }, |
| 2358 | 2374 | { "autosync-tries", 0, 16, 0, 0, "1" }, |
| 2359 | 2375 | { "binary-glob", 0, 40, 1, 0, "" }, |
| 2360 | - { "clearsign", 0, 0, 0, 0, "off" }, | |
| 2361 | 2376 | #if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || \ |
| 2362 | 2377 | defined(__APPLE__) |
| 2363 | 2378 | { "case-sensitive", 0, 0, 0, 0, "off" }, |
| 2364 | 2379 | #else |
| 2365 | 2380 | { "case-sensitive", 0, 0, 0, 0, "on" }, |
| 2366 | 2381 | #endif |
| 2367 | 2382 | { "clean-glob", 0, 40, 1, 0, "" }, |
| 2383 | + { "clearsign", 0, 0, 0, 0, "off" }, | |
| 2368 | 2384 | { "crnl-glob", 0, 40, 1, 0, "" }, |
| 2369 | 2385 | { "default-perms", 0, 16, 0, 0, "u" }, |
| 2370 | 2386 | { "diff-binary", 0, 0, 0, 0, "on" }, |
| 2371 | 2387 | { "diff-command", 0, 40, 0, 0, "" }, |
| 2372 | 2388 | { "dont-push", 0, 0, 0, 0, "off" }, |
| @@ -2407,10 +2423,41 @@ | ||
| 2407 | 2423 | { "th1-uri-regexp", 0, 40, 1, 0, "" }, |
| 2408 | 2424 | { "web-browser", 0, 32, 0, 0, "" }, |
| 2409 | 2425 | { "white-foreground", 0, 0, 0, 0, "off" }, |
| 2410 | 2426 | { 0,0,0,0,0,0 } |
| 2411 | 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 | +} | |
| 2412 | 2459 | |
| 2413 | 2460 | /* |
| 2414 | 2461 | ** COMMAND: settings |
| 2415 | 2462 | ** COMMAND: unset* |
| 2416 | 2463 | ** |
| @@ -2658,61 +2705,65 @@ | ||
| 2658 | 2705 | globalFlag = 1; |
| 2659 | 2706 | } |
| 2660 | 2707 | if( unsetFlag && g.argc!=3 ){ |
| 2661 | 2708 | usage("PROPERTY ?-global?"); |
| 2662 | 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 | + | |
| 2663 | 2722 | 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]); | |
| 2667 | 2725 | } |
| 2668 | 2726 | }else if( g.argc==3 || g.argc==4 ){ |
| 2669 | 2727 | 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 ){ | |
| 2676 | 2731 | fossil_fatal("no such setting: %s", zName); |
| 2677 | 2732 | } |
| 2678 | - isManifest = fossil_strcmp(ctrlSettings[i].name, "manifest")==0; | |
| 2679 | - if( isManifest && globalFlag ){ | |
| 2733 | + if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){ | |
| 2680 | 2734 | fossil_fatal("cannot set 'manifest' globally"); |
| 2681 | 2735 | } |
| 2682 | 2736 | 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 | + } | |
| 2714 | 2765 | } |
| 2715 | 2766 | }else{ |
| 2716 | 2767 | usage("?PROPERTY? ?VALUE? ?-global?"); |
| 2717 | 2768 | } |
| 2718 | 2769 | } |
| 2719 | 2770 |
| --- 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 @@ | ||
| 65 | 65 | } |
| 66 | 66 | } |
| 67 | 67 | if( i>=n ){ |
| 68 | 68 | return 0; /* Plain text */ |
| 69 | 69 | } |
| 70 | - for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ | |
| 70 | + for(i=0; i<ArraySize(aMime); i++){ | |
| 71 | 71 | if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ |
| 72 | 72 | return aMime[i].zMimetype; |
| 73 | 73 | } |
| 74 | 74 | } |
| 75 | 75 | return "unknown/unknown"; |
| 76 | 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 | +}; | |
| 77 | 293 | |
| 78 | 294 | /* |
| 79 | 295 | ** Guess the mime-type of a document based on its name. |
| 80 | 296 | */ |
| 81 | 297 | const char *mimetype_from_name(const char *zName){ |
| @@ -83,226 +299,17 @@ | ||
| 83 | 299 | int i; |
| 84 | 300 | int first, last; |
| 85 | 301 | int len; |
| 86 | 302 | char zSuffix[20]; |
| 87 | 303 | |
| 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 | 304 | |
| 298 | 305 | #ifdef FOSSIL_DEBUG |
| 299 | 306 | /* This is test code to make sure the table above is in the correct |
| 300 | 307 | ** order |
| 301 | 308 | */ |
| 302 | 309 | 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++){ | |
| 304 | 311 | if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){ |
| 305 | 312 | fossil_fatal("mimetypes out of sequence: %s before %s", |
| 306 | 313 | aMime[i-1].zSuffix, aMime[i].zSuffix); |
| 307 | 314 | } |
| 308 | 315 | } |
| @@ -317,11 +324,11 @@ | ||
| 317 | 324 | len = strlen(z); |
| 318 | 325 | if( len<sizeof(zSuffix)-1 ){ |
| 319 | 326 | sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z); |
| 320 | 327 | for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); |
| 321 | 328 | first = 0; |
| 322 | - last = sizeof(aMime)/sizeof(aMime[0]) - 1; | |
| 329 | + last = ArraySize(aMime) - 1; | |
| 323 | 330 | while( first<=last ){ |
| 324 | 331 | int c; |
| 325 | 332 | i = (first+last)/2; |
| 326 | 333 | c = fossil_strcmp(zSuffix, aMime[i].zSuffix); |
| 327 | 334 | if( c==0 ) return aMime[i].zMimetype; |
| @@ -350,10 +357,134 @@ | ||
| 350 | 357 | int i; |
| 351 | 358 | for(i=2; i<g.argc; i++){ |
| 352 | 359 | fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); |
| 353 | 360 | } |
| 354 | 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 | +} | |
| 355 | 486 | |
| 356 | 487 | /* |
| 357 | 488 | ** WEBPAGE: doc |
| 358 | 489 | ** URL: /doc?name=CHECKIN/FILE |
| 359 | 490 | ** URL: /doc/CHECKIN/FILE |
| @@ -375,97 +506,80 @@ | ||
| 375 | 506 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 376 | 507 | ** for looking at what a file will look like using the /doc webpage after |
| 377 | 508 | ** it gets checked in. |
| 378 | 509 | ** |
| 379 | 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. | |
| 380 | 516 | */ |
| 381 | 517 | void doc_page(void){ |
| 382 | 518 | const char *zName; /* Argument to the /doc page */ |
| 383 | - const char *zOrigName; /* Original document name */ | |
| 519 | + const char *zOrigName = "?"; /* Original document name */ | |
| 384 | 520 | const char *zMime; /* Document MIME type */ |
| 385 | - char *zCheckin; /* The checkin holding the document */ | |
| 521 | + char *zCheckin = "tip"; /* The checkin holding the document */ | |
| 386 | 522 | int vid = 0; /* Artifact of checkin */ |
| 387 | 523 | int rid = 0; /* Artifact of file */ |
| 388 | 524 | int i; /* Loop counter */ |
| 389 | 525 | 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 | + }; | |
| 391 | 531 | |
| 392 | 532 | login_check_credentials(); |
| 393 | 533 | 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 ){ | |
| 426 | 572 | rid = 1; /* Fake RID just to get the loop to end */ |
| 427 | 573 | } |
| 428 | 574 | 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; | |
| 467 | 581 | blob_to_utf8_no_bom(&filebody, 0); |
| 468 | 582 | |
| 469 | 583 | /* The file is now contained in the filebody blob. Deliver the |
| 470 | 584 | ** file to the user |
| 471 | 585 | */ |
| @@ -477,11 +591,11 @@ | ||
| 477 | 591 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| 478 | 592 | " FROM blob WHERE rid=%d", vid)); |
| 479 | 593 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 480 | 594 | " WHERE objid=%d AND type='ci'", vid)); |
| 481 | 595 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 482 | - Blob title, tail; | |
| 596 | + Blob tail; | |
| 483 | 597 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 484 | 598 | if( wiki_find_title(&filebody, &title, &tail) ){ |
| 485 | 599 | style_header("%s", blob_str(&title)); |
| 486 | 600 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 487 | 601 | }else{ |
| @@ -488,26 +602,32 @@ | ||
| 488 | 602 | style_header("Documentation"); |
| 489 | 603 | wiki_convert(&filebody, 0, WIKI_BUTTONS); |
| 490 | 604 | } |
| 491 | 605 | style_footer(); |
| 492 | 606 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 493 | - Blob title = BLOB_INITIALIZER; | |
| 494 | 607 | Blob tail = BLOB_INITIALIZER; |
| 495 | 608 | markdown_to_html(&filebody, &title, &tail); |
| 496 | 609 | if( blob_size(&title)>0 ){ |
| 497 | 610 | style_header("%s", blob_str(&title)); |
| 498 | 611 | }else{ |
| 499 | - style_header("%s", nMiss?"Not Found":"Documentation"); | |
| 612 | + style_header("%s", nMiss>=ArraySize(azSuffix)? | |
| 613 | + "Not Found" : "Documentation"); | |
| 500 | 614 | } |
| 501 | 615 | blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); |
| 502 | 616 | style_footer(); |
| 503 | 617 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 504 | 618 | style_header("Documentation"); |
| 505 | 619 | @ <blockquote><pre> |
| 506 | 620 | @ %h(blob_str(&filebody)) |
| 507 | 621 | @ </pre></blockquote> |
| 508 | 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(); | |
| 509 | 629 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 510 | 630 | }else if( db_get_boolean("th1-docs", 0) && |
| 511 | 631 | fossil_strcmp(zMime, "application/x-th1")==0 ){ |
| 512 | 632 | style_header("%h", zName); |
| 513 | 633 | Th_Render(blob_str(&filebody)); |
| @@ -515,11 +635,12 @@ | ||
| 515 | 635 | #endif |
| 516 | 636 | }else{ |
| 517 | 637 | cgi_set_content_type(zMime); |
| 518 | 638 | cgi_set_content(&filebody); |
| 519 | 639 | } |
| 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); | |
| 521 | 642 | return; |
| 522 | 643 | |
| 523 | 644 | /* Jump here when unable to locate the document */ |
| 524 | 645 | doc_not_found: |
| 525 | 646 | db_end_transaction(0); |
| @@ -528,10 +649,11 @@ | ||
| 528 | 649 | @ <p>Document %h(zOrigName) not found |
| 529 | 650 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 530 | 651 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 531 | 652 | } |
| 532 | 653 | style_footer(); |
| 654 | + db_end_transaction(0); | |
| 533 | 655 | return; |
| 534 | 656 | } |
| 535 | 657 | |
| 536 | 658 | /* |
| 537 | 659 | ** The default logo. |
| @@ -653,5 +775,18 @@ | ||
| 653 | 775 | } |
| 654 | 776 | cgi_set_content_type(zMime); |
| 655 | 777 | cgi_set_content(&bgimg); |
| 656 | 778 | g.isConst = 1; |
| 657 | 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 | +} | |
| 658 | 793 |
| --- 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 @@ | ||
| 44 | 44 | const char *zSep = "("; |
| 45 | 45 | int nTerm = 0; |
| 46 | 46 | int i; |
| 47 | 47 | int cTerm; |
| 48 | 48 | |
| 49 | - if( zGlobList==0 || zGlobList[0]==0 ) return "0"; | |
| 49 | + if( zGlobList==0 || zGlobList[0]==0 ) return fossil_strdup("0"); | |
| 50 | 50 | blob_zero(&expr); |
| 51 | 51 | while( zGlobList[0] ){ |
| 52 | 52 | while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){ |
| 53 | 53 | zGlobList++; /* Skip leading commas, spaces, and newlines */ |
| 54 | 54 | } |
| @@ -73,11 +73,11 @@ | ||
| 73 | 73 | } |
| 74 | 74 | if( nTerm ){ |
| 75 | 75 | blob_appendf(&expr, ")"); |
| 76 | 76 | return blob_str(&expr); |
| 77 | 77 | }else{ |
| 78 | - return "0"; | |
| 78 | + return fossil_strdup("0"); | |
| 79 | 79 | } |
| 80 | 80 | } |
| 81 | 81 | |
| 82 | 82 | #if INTERFACE |
| 83 | 83 | /* |
| 84 | 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 "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 |
+7
-2
| --- src/json_config.c | ||
| +++ src/json_config.c | ||
| @@ -60,24 +60,29 @@ | ||
| 60 | 60 | { "footer", CONFIGSET_SKIN }, |
| 61 | 61 | { "logo-mimetype", CONFIGSET_SKIN }, |
| 62 | 62 | { "logo-image", CONFIGSET_SKIN }, |
| 63 | 63 | { "background-mimetype", CONFIGSET_SKIN }, |
| 64 | 64 | { "background-image", CONFIGSET_SKIN }, |
| 65 | -{ "index-page", CONFIGSET_SKIN }, | |
| 66 | 65 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 67 | 66 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 68 | 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 }, | |
| 69 | 72 | |
| 70 | 73 | { "project-name", CONFIGSET_PROJ }, |
| 74 | +{ "short-project-name", CONFIGSET_PROJ }, | |
| 71 | 75 | { "project-description", CONFIGSET_PROJ }, |
| 76 | +{ "index-page", CONFIGSET_PROJ }, | |
| 72 | 77 | { "manifest", CONFIGSET_PROJ }, |
| 73 | 78 | { "binary-glob", CONFIGSET_PROJ }, |
| 74 | 79 | { "clean-glob", CONFIGSET_PROJ }, |
| 75 | -{ "encoding-glob", CONFIGSET_PROJ }, | |
| 76 | 80 | { "ignore-glob", CONFIGSET_PROJ }, |
| 77 | 81 | { "keep-glob", CONFIGSET_PROJ }, |
| 78 | 82 | { "crnl-glob", CONFIGSET_PROJ }, |
| 83 | +{ "encoding-glob", CONFIGSET_PROJ }, | |
| 79 | 84 | { "empty-dirs", CONFIGSET_PROJ }, |
| 80 | 85 | { "allow-symlinks", CONFIGSET_PROJ }, |
| 81 | 86 | |
| 82 | 87 | { "ticket-table", CONFIGSET_TKT }, |
| 83 | 88 | { "ticket-common", CONFIGSET_TKT }, |
| 84 | 89 |
| --- 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 @@ | ||
| 449 | 449 | -DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 450 | 450 | -DSQLITE_ENABLE_LOCKING_STYLE=0 \ |
| 451 | 451 | -DSQLITE_THREADSAFE=0 \ |
| 452 | 452 | -DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 453 | 453 | -DSQLITE_OMIT_DEPRECATED \ |
| 454 | - -DSQLITE_ENABLE_EXPLAIN_COMMENTS | |
| 454 | + -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ | |
| 455 | + -DSQLITE_ENABLE_FTS4 | |
| 455 | 456 | |
| 456 | 457 | # Setup the options used to compile the included SQLite shell. |
| 457 | 458 | SHELL_OPTIONS = -Dmain=sqlite3_shell \ |
| 458 | 459 | -DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 459 | 460 | -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ |
| 460 | 461 |
| --- 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 |
+3
-2
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -157,10 +157,11 @@ | ||
| 157 | 157 | -DSQLITE_ENABLE_LOCKING_STYLE=0 |
| 158 | 158 | -DSQLITE_THREADSAFE=0 |
| 159 | 159 | -DSQLITE_DEFAULT_FILE_FORMAT=4 |
| 160 | 160 | -DSQLITE_OMIT_DEPRECATED |
| 161 | 161 | -DSQLITE_ENABLE_EXPLAIN_COMMENTS |
| 162 | + -DSQLITE_ENABLE_FTS4 | |
| 162 | 163 | } |
| 163 | 164 | #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1 |
| 164 | 165 | #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4 |
| 165 | 166 | #lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI |
| 166 | 167 | #lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096 |
| @@ -585,11 +586,11 @@ | ||
| 585 | 586 | #### The directories where the OpenSSL include and library files are located. |
| 586 | 587 | # The recommended usage here is to use the Sysinternals junction tool |
| 587 | 588 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 588 | 589 | # Fossil source code directory and the target OpenSSL source directory. |
| 589 | 590 | # |
| 590 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l | |
| 591 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 591 | 592 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 592 | 593 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 593 | 594 | |
| 594 | 595 | #### Either the directory where the Tcl library is installed or the Tcl |
| 595 | 596 | # source code directory resides (depending on the value of the macro |
| @@ -1318,11 +1319,11 @@ | ||
| 1318 | 1319 | |
| 1319 | 1320 | # Uncomment to enable Tcl support |
| 1320 | 1321 | # FOSSIL_ENABLE_TCL = 1 |
| 1321 | 1322 | |
| 1322 | 1323 | !ifdef FOSSIL_ENABLE_SSL |
| 1323 | -SSLDIR = $(B)\compat\openssl-1.0.1l | |
| 1324 | +SSLDIR = $(B)\compat\openssl-1.0.2 | |
| 1324 | 1325 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1325 | 1326 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1326 | 1327 | SSLLFLAGS = /nologo /opt:ref /debug |
| 1327 | 1328 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1328 | 1329 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| 1329 | 1330 |
| --- 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 |
+5
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -600,10 +600,11 @@ | ||
| 600 | 600 | if( i>0 && fossil_strcmp(p->aField[i-1].zName, zName)>=0 ){ |
| 601 | 601 | SYNTAX("incorrect J-card sort order"); |
| 602 | 602 | } |
| 603 | 603 | break; |
| 604 | 604 | } |
| 605 | + | |
| 605 | 606 | |
| 606 | 607 | /* |
| 607 | 608 | ** K <uuid> |
| 608 | 609 | ** |
| 609 | 610 | ** A K-line gives the UUID for the ticket which this control file |
| @@ -846,10 +847,11 @@ | ||
| 846 | 847 | if( x.z[0]!='\n' ) SYNTAX("W-card content no \\n terminated"); |
| 847 | 848 | x.z[0] = 0; |
| 848 | 849 | x.z++; |
| 849 | 850 | break; |
| 850 | 851 | } |
| 852 | + | |
| 851 | 853 | |
| 852 | 854 | /* |
| 853 | 855 | ** Z <md5sum> |
| 854 | 856 | ** |
| 855 | 857 | ** MD5 checksum on this control file. The checksum is over all |
| @@ -1827,10 +1829,11 @@ | ||
| 1827 | 1829 | for(i=0; i<p->nFile; i++){ |
| 1828 | 1830 | add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, |
| 1829 | 1831 | isPublic, 1, manifest_file_mperm(&p->aFile[i])); |
| 1830 | 1832 | } |
| 1831 | 1833 | } |
| 1834 | + search_doc_touch('c', rid, 0); | |
| 1832 | 1835 | db_multi_exec( |
| 1833 | 1836 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1834 | 1837 | "bgcolor,euser,ecomment,omtime)" |
| 1835 | 1838 | "VALUES('ci'," |
| 1836 | 1839 | " coalesce(" |
| @@ -1932,10 +1935,11 @@ | ||
| 1932 | 1935 | if( nWiki>0 ){ |
| 1933 | 1936 | zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); |
| 1934 | 1937 | }else{ |
| 1935 | 1938 | zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); |
| 1936 | 1939 | } |
| 1940 | + search_doc_touch('w',rid,p->zWikiTitle); | |
| 1937 | 1941 | db_multi_exec( |
| 1938 | 1942 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1939 | 1943 | " bgcolor,euser,ecomment)" |
| 1940 | 1944 | "VALUES('w',%.17g,%d,%Q,%Q," |
| 1941 | 1945 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," |
| @@ -1984,10 +1988,11 @@ | ||
| 1984 | 1988 | } |
| 1985 | 1989 | } |
| 1986 | 1990 | if( subsequent ){ |
| 1987 | 1991 | content_deltify(rid, subsequent, 0); |
| 1988 | 1992 | }else{ |
| 1993 | + search_doc_touch('e',rid,0); | |
| 1989 | 1994 | db_multi_exec( |
| 1990 | 1995 | "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" |
| 1991 | 1996 | "VALUES('e',%.17g,%d,%d,%Q,%Q," |
| 1992 | 1997 | " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", |
| 1993 | 1998 | p->rEventDate, rid, tagid, p->zUser, p->zComment, |
| 1994 | 1999 |
| --- 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 @@ | ||
| 533 | 533 | ** --vacuum Run VACUUM on the database after rebuilding |
| 534 | 534 | ** --deanalyze Remove ANALYZE tables from the database |
| 535 | 535 | ** --analyze Run ANALYZE on the database after rebuilding |
| 536 | 536 | ** --wal Set Write-Ahead-Log journalling mode on the database |
| 537 | 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 | |
| 538 | 540 | ** |
| 539 | 541 | ** See also: deconstruct, reconstruct |
| 540 | 542 | */ |
| 541 | 543 | void rebuild_database(void){ |
| 542 | 544 | int forceFlag; |
| @@ -550,10 +552,11 @@ | ||
| 550 | 552 | int runVacuum; |
| 551 | 553 | int runDeanalyze; |
| 552 | 554 | int runAnalyze; |
| 553 | 555 | int runCompress; |
| 554 | 556 | int showStats; |
| 557 | + int runReindex; | |
| 555 | 558 | |
| 556 | 559 | omitVerify = find_option("noverify",0,0)!=0; |
| 557 | 560 | forceFlag = find_option("force","f",0)!=0; |
| 558 | 561 | randomizeFlag = find_option("randomize", 0, 0)!=0; |
| 559 | 562 | doClustering = find_option("cluster", 0, 0)!=0; |
| @@ -580,15 +583,19 @@ | ||
| 580 | 583 | usage("?REPOSITORY-FILENAME?"); |
| 581 | 584 | } |
| 582 | 585 | db_close(1); |
| 583 | 586 | db_open_repository(g.zRepositoryName); |
| 584 | 587 | } |
| 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 | + | |
| 586 | 592 | /* We should be done with options.. */ |
| 587 | 593 | verify_all_options(); |
| 588 | 594 | |
| 589 | 595 | db_begin_transaction(); |
| 596 | + search_drop_index(); | |
| 590 | 597 | ttyOutput = 1; |
| 591 | 598 | errCnt = rebuild_db(randomizeFlag, 1, doClustering); |
| 592 | 599 | reconstruct_private_table(); |
| 593 | 600 | db_multi_exec( |
| 594 | 601 | "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());" |
| @@ -634,10 +641,11 @@ | ||
| 634 | 641 | } |
| 635 | 642 | if( activateWal ){ |
| 636 | 643 | db_multi_exec("PRAGMA journal_mode=WAL;"); |
| 637 | 644 | } |
| 638 | 645 | } |
| 646 | + if( runReindex ) search_rebuild_index(); | |
| 639 | 647 | if( showStats ){ |
| 640 | 648 | static const struct { int idx; const char *zLabel; } aStat[] = { |
| 641 | 649 | { CFTYPE_ANY, "Artifacts:" }, |
| 642 | 650 | { CFTYPE_MANIFEST, "Manifests:" }, |
| 643 | 651 | { CFTYPE_CLUSTER, "Clusters:" }, |
| @@ -799,11 +807,11 @@ | ||
| 799 | 807 | int privateOnly = find_option("private",0,0)!=0; |
| 800 | 808 | int bNeedRebuild = 0; |
| 801 | 809 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); |
| 802 | 810 | db_close(1); |
| 803 | 811 | db_open_repository(g.zRepositoryName); |
| 804 | - | |
| 812 | + | |
| 805 | 813 | /* We should be done with options.. */ |
| 806 | 814 | verify_all_options(); |
| 807 | 815 | |
| 808 | 816 | if( !bForce ){ |
| 809 | 817 | Blob ans; |
| @@ -929,11 +937,11 @@ | ||
| 929 | 937 | fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); |
| 930 | 938 | usage("FILENAME DIRECTORY"); |
| 931 | 939 | } |
| 932 | 940 | db_create_repository(g.argv[2]); |
| 933 | 941 | db_open_repository(g.argv[2]); |
| 934 | - | |
| 942 | + | |
| 935 | 943 | /* We should be done with options.. */ |
| 936 | 944 | verify_all_options(); |
| 937 | 945 | |
| 938 | 946 | db_open_config(0); |
| 939 | 947 | db_begin_transaction(); |
| 940 | 948 |
| --- 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 |
+3
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -29,10 +29,12 @@ | ||
| 29 | 29 | # define SQLITE_RECURSIVE 33 |
| 30 | 30 | #endif |
| 31 | 31 | |
| 32 | 32 | /* |
| 33 | 33 | ** WEBPAGE: /reportlist |
| 34 | +** | |
| 35 | +** Main menu for Tickets. | |
| 34 | 36 | */ |
| 35 | 37 | void view_list(void){ |
| 36 | 38 | const char *zScript; |
| 37 | 39 | Blob ril; /* Report Item List */ |
| 38 | 40 | Stmt q; |
| @@ -40,10 +42,11 @@ | ||
| 40 | 42 | int cnt = 0; |
| 41 | 43 | |
| 42 | 44 | login_check_credentials(); |
| 43 | 45 | if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(); return; } |
| 44 | 46 | style_header("Ticket Main Menu"); |
| 47 | + ticket_standard_submenu(T_ALL_BUT(T_REPLIST)); | |
| 45 | 48 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1); |
| 46 | 49 | zScript = ticket_reportlist_code(); |
| 47 | 50 | if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1); |
| 48 | 51 | |
| 49 | 52 | blob_zero(&ril); |
| 50 | 53 |
| --- 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 @@ | ||
| 65 | 65 | @ FROM event, blob |
| 66 | 66 | @ WHERE blob.rid=event.objid |
| 67 | 67 | ; |
| 68 | 68 | |
| 69 | 69 | 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 ){ | |
| 71 | 71 | return; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 74 | blob_zero(&bSQL); |
| 75 | 75 | blob_append( &bSQL, zSQL1, -1 ); |
| @@ -83,11 +83,11 @@ | ||
| 83 | 83 | if( !g.perm.Read ){ |
| 84 | 84 | if( g.perm.RdTkt && g.perm.RdWiki ){ |
| 85 | 85 | blob_append(&bSQL, " AND event.type!='ci'", -1); |
| 86 | 86 | }else if( g.perm.RdTkt ){ |
| 87 | 87 | blob_append(&bSQL, " AND event.type=='t'", -1); |
| 88 | - | |
| 88 | + | |
| 89 | 89 | }else{ |
| 90 | 90 | blob_append(&bSQL, " AND event.type=='w'", -1); |
| 91 | 91 | } |
| 92 | 92 | }else if( !g.perm.RdWiki ){ |
| 93 | 93 | if( g.perm.RdTkt ){ |
| 94 | 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 |
| --- 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 @@ | ||
| 230 | 230 | @ name TEXT UNIQUE -- Name of file page |
| 231 | 231 | @ ); |
| 232 | 232 | @ |
| 233 | 233 | @ -- Linkages between checkins, files created by each checkin, and |
| 234 | 234 | @ -- the names of those files. |
| 235 | -@ -- | |
| 235 | +@ -- | |
| 236 | 236 | @ -- Each entry represents a file that changed content from pid to fid |
| 237 | 237 | @ -- due to the check-in that goes from pmid to mid. fnid is the name |
| 238 | 238 | @ -- of the file in the mid check-in. If the file was renamed as part |
| 239 | 239 | @ -- of the mid check-in, then pfnid is the previous filename. |
| 240 | 240 | @ |
| 241 | 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 |
| --- 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 @@ | ||
| 40 | 40 | struct srchTerm { /* For each search term */ |
| 41 | 41 | char *z; /* Text */ |
| 42 | 42 | int n; /* length */ |
| 43 | 43 | } a[SEARCH_MAX_TERM]; |
| 44 | 44 | /* Snippet controls */ |
| 45 | - char *zMarkBegin; /* Start of a match */ | |
| 45 | + char *zPattern; /* The search pattern */ | |
| 46 | + char *zMarkBegin; /* Start of a match */ | |
| 46 | 47 | char *zMarkEnd; /* End of a match */ |
| 47 | 48 | char *zMarkGap; /* A gap between two matches */ |
| 48 | 49 | unsigned fSrchFlg; /* Flags */ |
| 50 | + int iScore; /* Score of the last match attempt */ | |
| 51 | + Blob snip; /* Snippet for the most recent match */ | |
| 49 | 52 | }; |
| 50 | 53 | |
| 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 */ | |
| 52 | 56 | |
| 53 | 57 | #endif |
| 54 | 58 | |
| 59 | +/* | |
| 60 | +** There is a single global Search object: | |
| 61 | +*/ | |
| 62 | +static Search gSearch; | |
| 63 | + | |
| 55 | 64 | |
| 56 | 65 | /* |
| 57 | 66 | ** Theses characters constitute a word boundary |
| 58 | 67 | */ |
| 59 | 68 | static const char isBoundary[] = { |
| @@ -74,23 +83,53 @@ | ||
| 74 | 83 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 75 | 84 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 76 | 85 | }; |
| 77 | 86 | #define ISALNUM(x) (!isBoundary[(x)&0xff]) |
| 78 | 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 | + | |
| 79 | 104 | /* |
| 80 | 105 | ** Compile a search pattern |
| 81 | 106 | */ |
| 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 | +){ | |
| 84 | 114 | Search *p; |
| 85 | 115 | char *z; |
| 86 | 116 | int i; |
| 87 | 117 | |
| 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); | |
| 92 | 131 | while( *z && p->nTerm<SEARCH_MAX_TERM ){ |
| 93 | 132 | while( *z && !ISALNUM(*z) ){ z++; } |
| 94 | 133 | if( *z==0 ) break; |
| 95 | 134 | p->a[p->nTerm].z = z; |
| 96 | 135 | for(i=1; ISALNUM(z[i]); i++){} |
| @@ -100,49 +139,46 @@ | ||
| 100 | 139 | } |
| 101 | 140 | return p; |
| 102 | 141 | } |
| 103 | 142 | |
| 104 | 143 | |
| 105 | -/* | |
| 106 | -** Destroy a search context. | |
| 107 | -*/ | |
| 108 | -void search_end(Search *p){ | |
| 109 | - free(p); | |
| 110 | -} | |
| 111 | - | |
| 112 | 144 | /* |
| 113 | 145 | ** Append n bytes of text to snippet zTxt. Encode the text appropriately. |
| 114 | 146 | */ |
| 115 | 147 | static void snippet_text_append( |
| 116 | 148 | Search *p, /* The search context */ |
| 117 | 149 | Blob *pSnip, /* Append to this snippet */ |
| 118 | 150 | const char *zTxt, /* Text to append */ |
| 119 | 151 | int n /* How many bytes to append */ |
| 120 | 152 | ){ |
| 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 | + } | |
| 125 | 159 | } |
| 126 | 160 | } |
| 127 | 161 | |
| 128 | 162 | /* |
| 129 | 163 | ** 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. | |
| 132 | 169 | ** |
| 133 | 170 | ** Scoring: |
| 134 | 171 | ** * All terms must match at least once or the score is zero |
| 135 | 172 | ** * One point for each matching term |
| 136 | 173 | ** * Extra points if consecutive words of the pattern are consecutive |
| 137 | 174 | ** in the document |
| 138 | 175 | */ |
| 139 | -static int search_score( | |
| 176 | +static int search_match( | |
| 140 | 177 | Search *p, /* Search pattern and flags */ |
| 141 | 178 | 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 */ | |
| 144 | 180 | ){ |
| 145 | 181 | int score; /* Final score */ |
| 146 | 182 | int i; /* Offset into current document */ |
| 147 | 183 | int ii; /* Loop counter */ |
| 148 | 184 | int j; /* Loop over search terms */ |
| @@ -186,25 +222,26 @@ | ||
| 186 | 222 | } |
| 187 | 223 | break; |
| 188 | 224 | } |
| 189 | 225 | } |
| 190 | 226 | while( ISALNUM(zDoc[i]) ){ i++; } |
| 227 | + if( zDoc[i]==0 ) break; | |
| 191 | 228 | } |
| 192 | 229 | } |
| 193 | 230 | |
| 194 | 231 | /* 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 | |
| 196 | 233 | */ |
| 197 | 234 | score = 1; |
| 198 | 235 | 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; | |
| 200 | 239 | |
| 201 | 240 | |
| 202 | 241 | /* Prepare a snippet that describes the matching text. |
| 203 | 242 | */ |
| 204 | - blob_init(pSnip, 0, 0); | |
| 205 | - | |
| 206 | 243 | while(1){ |
| 207 | 244 | int iOfst; |
| 208 | 245 | int iTail; |
| 209 | 246 | int iBest; |
| 210 | 247 | for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){} |
| @@ -239,11 +276,11 @@ | ||
| 239 | 276 | if( iOfst<0 ) iOfst = 0; |
| 240 | 277 | while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--; |
| 241 | 278 | while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++; |
| 242 | 279 | for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){} |
| 243 | 280 | 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); | |
| 245 | 282 | wantGap = zDoc[iTail]!=0; |
| 246 | 283 | zDoc += iOfst; |
| 247 | 284 | iTail -= iOfst; |
| 248 | 285 | |
| 249 | 286 | /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */ |
| @@ -252,94 +289,196 @@ | ||
| 252 | 289 | for(j=0; j<p->nTerm; j++){ |
| 253 | 290 | int n = p->a[j].n; |
| 254 | 291 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 |
| 255 | 292 | && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') |
| 256 | 293 | ){ |
| 257 | - snippet_text_append(p, pSnip, zDoc, i); | |
| 294 | + snippet_text_append(p, &p->snip, zDoc, i); | |
| 258 | 295 | zDoc += i; |
| 259 | 296 | iTail -= i; |
| 260 | - blob_append(pSnip, p->zMarkBegin, -1); | |
| 297 | + blob_append(&p->snip, p->zMarkBegin, -1); | |
| 261 | 298 | if( p->a[j].z[n]=='*' ){ |
| 262 | 299 | while( ISALNUM(zDoc[n]) ) n++; |
| 263 | 300 | } |
| 264 | - snippet_text_append(p, pSnip, zDoc, n); | |
| 301 | + snippet_text_append(p, &p->snip, zDoc, n); | |
| 265 | 302 | zDoc += n; |
| 266 | 303 | iTail -= n; |
| 267 | - blob_append(pSnip, p->zMarkEnd, -1); | |
| 304 | + blob_append(&p->snip, p->zMarkEnd, -1); | |
| 268 | 305 | i = -1; |
| 269 | 306 | break; |
| 270 | 307 | } /* end-if */ |
| 271 | 308 | } /* end for(j) */ |
| 272 | 309 | if( j<p->nTerm ){ |
| 273 | 310 | while( ISALNUM(zDoc[i]) && i<iTail ){ i++; } |
| 274 | 311 | } |
| 275 | 312 | } /* end for(i) */ |
| 276 | - if( iTail>0 ) snippet_text_append(p, pSnip, zDoc, iTail); | |
| 313 | + snippet_text_append(p, &p->snip, zDoc, iTail); | |
| 277 | 314 | } |
| 278 | - if( wantGap ) blob_append(pSnip, p->zMarkGap, -1); | |
| 315 | + if( wantGap ) blob_append(&p->snip, p->zMarkGap, -1); | |
| 279 | 316 | return score; |
| 280 | 317 | } |
| 281 | 318 | |
| 282 | 319 | /* |
| 283 | -** COMMAND: test-snippet | |
| 320 | +** COMMAND: test-match | |
| 284 | 321 | ** |
| 285 | -** Usage: fossil test-snippet SEARCHSTRING FILE1 FILE2 ... | |
| 322 | +** Usage: fossil test-match SEARCHSTRING FILE1 FILE2 ... | |
| 286 | 323 | */ |
| 287 | -void test_snippet_cmd(void){ | |
| 324 | +void test_match_cmd(void){ | |
| 288 | 325 | Search *p; |
| 289 | 326 | int i; |
| 290 | 327 | Blob x; |
| 291 | - Blob snip; | |
| 292 | 328 | int score; |
| 293 | 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(); | |
| 294 | 337 | 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); | |
| 299 | 342 | for(i=3; i<g.argc; i++){ |
| 300 | 343 | blob_read_from_file(&x, g.argv[i]); |
| 301 | 344 | 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); | |
| 304 | 347 | blob_reset(&x); |
| 305 | 348 | 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), '='); | |
| 308 | 350 | } |
| 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); | |
| 310 | 406 | } |
| 311 | 407 | |
| 312 | 408 | /* |
| 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. | |
| 315 | 411 | */ |
| 316 | 412 | static void search_score_sqlfunc( |
| 317 | 413 | sqlite3_context *context, |
| 318 | 414 | int argc, |
| 319 | 415 | sqlite3_value **argv |
| 320 | 416 | ){ |
| 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); | |
| 331 | 458 | } |
| 332 | 459 | |
| 333 | 460 | /* |
| 334 | 461 | ** Register the "score()" SQL function to score its input text |
| 335 | 462 | ** using the given Search object. Once this function is registered, |
| 336 | 463 | ** do not delete the Search object. |
| 337 | 464 | */ |
| 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, | |
| 340 | 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); | |
| 341 | 480 | } |
| 342 | 481 | |
| 343 | 482 | /* |
| 344 | 483 | ** Testing the search function. |
| 345 | 484 | ** |
| @@ -357,11 +496,10 @@ | ||
| 357 | 496 | ** of entries returned. The -width option can be |
| 358 | 497 | ** used to set the output width used when printing |
| 359 | 498 | ** matches. |
| 360 | 499 | */ |
| 361 | 500 | void search_cmd(void){ |
| 362 | - Search *p; | |
| 363 | 501 | Blob pattern; |
| 364 | 502 | int i; |
| 365 | 503 | Blob sql = empty_blob; |
| 366 | 504 | Stmt q; |
| 367 | 505 | int iBest; |
| @@ -386,13 +524,13 @@ | ||
| 386 | 524 | if( g.argc<2 ) return; |
| 387 | 525 | blob_init(&pattern, g.argv[2], -1); |
| 388 | 526 | for(i=3; i<g.argc; i++){ |
| 389 | 527 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 390 | 528 | } |
| 391 | - p = search_init(blob_str(&pattern)); | |
| 529 | + (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC); | |
| 392 | 530 | blob_reset(&pattern); |
| 393 | - search_sql_setup(p); | |
| 531 | + search_sql_setup(g.db); | |
| 394 | 532 | |
| 395 | 533 | db_multi_exec( |
| 396 | 534 | "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);" |
| 397 | 535 | "CREATE INDEX srch_idx1 ON srch(x);" |
| 398 | 536 | "INSERT INTO srch(rid,uuid,date,comment,x)" |
| @@ -414,5 +552,932 @@ | ||
| 414 | 552 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 415 | 553 | blob_reset(&sql); |
| 416 | 554 | print_timeline(&q, nLimit, width, 0); |
| 417 | 555 | db_finalize(&q); |
| 418 | 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 "<". 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], "<", 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 | +} | |
| 419 | 1484 |
| --- 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 "<". 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], "<", 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 @@ | ||
| 70 | 70 | @ <p class="generalError"><b>Configuration Error:</b> Please add |
| 71 | 71 | @ <tt><base href="$secureurl/$current_page"></tt> after |
| 72 | 72 | @ <tt><head></tt> in the <a href="setup_header">HTML header</a>!</p> |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | - @ <table border="0" cellspacing="7"> | |
| 75 | + @ <table border="0" cellspacing="3"> | |
| 76 | 76 | setup_menu_entry("Users", "setup_ulist", |
| 77 | 77 | "Grant privileges to individual users."); |
| 78 | 78 | setup_menu_entry("Access", "setup_access", |
| 79 | 79 | "Control access settings."); |
| 80 | 80 | setup_menu_entry("Configuration", "setup_config", |
| @@ -86,10 +86,12 @@ | ||
| 86 | 86 | setup_menu_entry("Login-Group", "setup_login_group", |
| 87 | 87 | "Manage single sign-on between this repository and others" |
| 88 | 88 | " on the same server"); |
| 89 | 89 | setup_menu_entry("Tickets", "tktsetup", |
| 90 | 90 | "Configure the trouble-ticketing system for this repository"); |
| 91 | + setup_menu_entry("Search","srchsetup", | |
| 92 | + "Configure the built-in search engine"); | |
| 91 | 93 | setup_menu_entry("Transfers", "xfersetup", |
| 92 | 94 | "Configure the transfer system for this repository"); |
| 93 | 95 | setup_menu_entry("Skins", "setup_skin", |
| 94 | 96 | "Select from a menu of prepackaged \"skins\" for the web interface"); |
| 95 | 97 | setup_menu_entry("CSS", "setup_editcss", |
| @@ -1354,11 +1356,11 @@ | ||
| 1354 | 1356 | |
| 1355 | 1357 | /* |
| 1356 | 1358 | ** WEBPAGE: setup_settings |
| 1357 | 1359 | */ |
| 1358 | 1360 | void setup_settings(void){ |
| 1359 | - struct stControlSettings const *pSet; | |
| 1361 | + Setting const *pSet; | |
| 1360 | 1362 | |
| 1361 | 1363 | login_check_credentials(); |
| 1362 | 1364 | if( !g.perm.Setup ){ |
| 1363 | 1365 | login_needed(); |
| 1364 | 1366 | } |
| @@ -1375,14 +1377,14 @@ | ||
| 1375 | 1377 | @ See the "fossil help setting" output below for further information on |
| 1376 | 1378 | @ the meaning of each setting.</p><hr /> |
| 1377 | 1379 | @ <form action="%s(g.zTop)/setup_settings" method="post"><div> |
| 1378 | 1380 | @ <table border="0"><tr><td valign="top"> |
| 1379 | 1381 | login_insert_csrf_secret(); |
| 1380 | - for(pSet=ctrlSettings; pSet->name!=0; pSet++){ | |
| 1382 | + for(pSet=aSetting; pSet->name!=0; pSet++){ | |
| 1381 | 1383 | if( pSet->width==0 ){ |
| 1382 | 1384 | int hasVersionableValue = pSet->versionable && |
| 1383 | - (db_get_do_versionable(pSet->name, NULL)!=0); | |
| 1385 | + (db_get_versioned(pSet->name, NULL)!=0); | |
| 1384 | 1386 | onoff_attribute(pSet->name, pSet->name, |
| 1385 | 1387 | pSet->var!=0 ? pSet->var : pSet->name, |
| 1386 | 1388 | is_truth(pSet->def), hasVersionableValue); |
| 1387 | 1389 | if( pSet->versionable ){ |
| 1388 | 1390 | @ (v)<br /> |
| @@ -1391,31 +1393,31 @@ | ||
| 1391 | 1393 | } |
| 1392 | 1394 | } |
| 1393 | 1395 | } |
| 1394 | 1396 | @ <br /><input type="submit" name="submit" value="Apply Changes" /> |
| 1395 | 1397 | @ </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++){ | |
| 1397 | 1399 | if( pSet->width!=0 && !pSet->versionable && !pSet->forceTextArea ){ |
| 1398 | 1400 | entry_attribute(pSet->name, /*pSet->width*/ 25, pSet->name, |
| 1399 | 1401 | pSet->var!=0 ? pSet->var : pSet->name, |
| 1400 | 1402 | (char*)pSet->def, 0); |
| 1401 | 1403 | @ <br /> |
| 1402 | 1404 | } |
| 1403 | 1405 | } |
| 1404 | - for(pSet=ctrlSettings; pSet->name!=0; pSet++){ | |
| 1406 | + for(pSet=aSetting; pSet->name!=0; pSet++){ | |
| 1405 | 1407 | if( pSet->width!=0 && !pSet->versionable && pSet->forceTextArea ){ |
| 1406 | 1408 | @<b>%s(pSet->name)</b><br /> |
| 1407 | 1409 | textarea_attribute("", /*rows*/ 3, /*cols*/ 50, pSet->name, |
| 1408 | 1410 | pSet->var!=0 ? pSet->var : pSet->name, |
| 1409 | 1411 | (char*)pSet->def, 0); |
| 1410 | 1412 | @ <br /> |
| 1411 | 1413 | } |
| 1412 | 1414 | } |
| 1413 | 1415 | @ </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++){ | |
| 1415 | 1417 | 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; | |
| 1417 | 1419 | @<b>%s(pSet->name)</b> (v)<br /> |
| 1418 | 1420 | textarea_attribute("", /*rows*/ 3, /*cols*/ 20, pSet->name, |
| 1419 | 1421 | pSet->var!=0 ? pSet->var : pSet->name, |
| 1420 | 1422 | (char*)pSet->def, hasVersionableValue); |
| 1421 | 1423 | @<br /> |
| @@ -2155,5 +2157,77 @@ | ||
| 2155 | 2157 | if(limit>0 && counter<limit){ |
| 2156 | 2158 | @ <div>%d(counter) entries shown.</div> |
| 2157 | 2159 | } |
| 2158 | 2160 | style_footer(); |
| 2159 | 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 | +} | |
| 2160 | 2234 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -70,11 +70,11 @@ | |
| 70 | @ <p class="generalError"><b>Configuration Error:</b> Please add |
| 71 | @ <tt><base href="$secureurl/$current_page"></tt> after |
| 72 | @ <tt><head></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><base href="$secureurl/$current_page"></tt> after |
| 72 | @ <tt><head></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 @@ | ||
| 26 | 26 | ** |
| 27 | 27 | ** To add new built-in skins: |
| 28 | 28 | ** |
| 29 | 29 | ** 1. Pick a name for the new skin. (Here we use "xyzzy"). |
| 30 | 30 | ** |
| 31 | -** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, | |
| 31 | +** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, | |
| 32 | 32 | ** and skins/xyzzy/footer.txt into the source tree. |
| 33 | 33 | ** |
| 34 | 34 | ** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to |
| 35 | 35 | ** rebuild the makefiles to reference the new CSS, headers, and footers. |
| 36 | 36 | ** |
| @@ -42,11 +42,11 @@ | ||
| 42 | 42 | char *zSQL; /* Filled in at run-time with SQL to insert this skin */ |
| 43 | 43 | } aBuiltinSkin[] = { |
| 44 | 44 | { "Default", "default", 0 }, |
| 45 | 45 | { "Plain Gray, No Logo", "plain_gray", 0 }, |
| 46 | 46 | { "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 }, | |
| 48 | 48 | { "Shadow boxes & Rounded Corners", "rounded1", 0 }, |
| 49 | 49 | { "Enhanced Default", "enhanced1", 0 }, |
| 50 | 50 | { "San Francisco Modern", "etienne1", 0 }, |
| 51 | 51 | { "Eagle", "eagle", 0 }, |
| 52 | 52 | }; |
| 53 | 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/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 |
+1
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -134,10 +134,11 @@ | ||
| 134 | 134 | const void *notUsed |
| 135 | 135 | ){ |
| 136 | 136 | add_content_sql_commands(db); |
| 137 | 137 | db_add_aux_functions(db); |
| 138 | 138 | re_add_sql_func(db); |
| 139 | + search_sql_setup(db); | |
| 139 | 140 | g.zMainDbType = "repository"; |
| 140 | 141 | foci_register(db); |
| 141 | 142 | g.repositoryOpen = 1; |
| 142 | 143 | g.db = db; |
| 143 | 144 | return SQLITE_OK; |
| 144 | 145 |
| --- 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 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 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 | |
| 3 | +** version 3.8.8.2. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -276,13 +276,13 @@ | ||
| 276 | 276 | ** |
| 277 | 277 | ** See also: [sqlite3_libversion()], |
| 278 | 278 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 279 | 279 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 280 | 280 | */ |
| 281 | -#define SQLITE_VERSION "3.8.8.1" | |
| 281 | +#define SQLITE_VERSION "3.8.8.2" | |
| 282 | 282 | #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" | |
| 284 | 284 | |
| 285 | 285 | /* |
| 286 | 286 | ** CAPI3REF: Run-Time Library Version Numbers |
| 287 | 287 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 288 | 288 | ** |
| @@ -19870,21 +19870,10 @@ | ||
| 19870 | 19870 | # define SQLITE_WIN32_VOLATILE |
| 19871 | 19871 | #else |
| 19872 | 19872 | # define SQLITE_WIN32_VOLATILE volatile |
| 19873 | 19873 | #endif |
| 19874 | 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 | 19875 | #endif /* _OS_WIN_H_ */ |
| 19887 | 19876 | |
| 19888 | 19877 | /************** End of os_win.h **********************************************/ |
| 19889 | 19878 | /************** Continuing where we left off in mutex_w32.c ******************/ |
| 19890 | 19879 | #endif |
| @@ -22444,11 +22433,11 @@ | ||
| 22444 | 22433 | #endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ |
| 22445 | 22434 | /******************************** End Unix Pthreads *************************/ |
| 22446 | 22435 | |
| 22447 | 22436 | |
| 22448 | 22437 | /********************************* Win32 Threads ****************************/ |
| 22449 | -#if SQLITE_OS_WIN_THREADS | |
| 22438 | +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 | |
| 22450 | 22439 | |
| 22451 | 22440 | #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ |
| 22452 | 22441 | #include <process.h> |
| 22453 | 22442 | |
| 22454 | 22443 | /* A running thread */ |
| @@ -22537,11 +22526,11 @@ | ||
| 22537 | 22526 | if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; |
| 22538 | 22527 | sqlite3_free(p); |
| 22539 | 22528 | return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; |
| 22540 | 22529 | } |
| 22541 | 22530 | |
| 22542 | -#endif /* SQLITE_OS_WIN_THREADS */ | |
| 22531 | +#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ | |
| 22543 | 22532 | /******************************** End Win32 Threads *************************/ |
| 22544 | 22533 | |
| 22545 | 22534 | |
| 22546 | 22535 | /********************************* Single-Threaded **************************/ |
| 22547 | 22536 | #ifndef SQLITE_THREADS_IMPLEMENTED |
| @@ -50206,11 +50195,11 @@ | ||
| 50206 | 50195 | int (*xBusy)(void*), /* Function to call when busy */ |
| 50207 | 50196 | void *pBusyArg, /* Context argument for xBusyHandler */ |
| 50208 | 50197 | int sync_flags, /* Flags for OsSync() (or 0) */ |
| 50209 | 50198 | u8 *zBuf /* Temporary buffer to use */ |
| 50210 | 50199 | ){ |
| 50211 | - int rc; /* Return code */ | |
| 50200 | + int rc = SQLITE_OK; /* Return code */ | |
| 50212 | 50201 | int szPage; /* Database page-size */ |
| 50213 | 50202 | WalIterator *pIter = 0; /* Wal iterator context */ |
| 50214 | 50203 | u32 iDbpage = 0; /* Next database page to write */ |
| 50215 | 50204 | u32 iFrame = 0; /* Wal frame containing data for iDbpage */ |
| 50216 | 50205 | u32 mxSafeFrame; /* Max frame that can be backfilled */ |
| @@ -50220,108 +50209,111 @@ | ||
| 50220 | 50209 | |
| 50221 | 50210 | szPage = walPagesize(pWal); |
| 50222 | 50211 | testcase( szPage<=32768 ); |
| 50223 | 50212 | testcase( szPage>=65536 ); |
| 50224 | 50213 | 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 | + } | |
| 50323 | 50315 | } |
| 50324 | 50316 | |
| 50325 | 50317 | /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the |
| 50326 | 50318 | ** entire wal file has been copied into the database file, then block |
| 50327 | 50319 | ** until all readers have finished using the wal file. This ensures that |
| @@ -50332,11 +50324,11 @@ | ||
| 50332 | 50324 | if( pInfo->nBackfill<pWal->hdr.mxFrame ){ |
| 50333 | 50325 | rc = SQLITE_BUSY; |
| 50334 | 50326 | }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ |
| 50335 | 50327 | u32 salt1; |
| 50336 | 50328 | sqlite3_randomness(4, &salt1); |
| 50337 | - assert( mxSafeFrame==pWal->hdr.mxFrame ); | |
| 50329 | + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); | |
| 50338 | 50330 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 50339 | 50331 | if( rc==SQLITE_OK ){ |
| 50340 | 50332 | if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ |
| 50341 | 50333 | /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as |
| 50342 | 50334 | ** SQLITE_CHECKPOINT_RESTART with the addition that it also |
| @@ -128378,10 +128370,11 @@ | ||
| 128378 | 128370 | } |
| 128379 | 128371 | if( iDb<0 ){ |
| 128380 | 128372 | rc = SQLITE_ERROR; |
| 128381 | 128373 | sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); |
| 128382 | 128374 | }else{ |
| 128375 | + db->busyHandler.nBusy = 0; | |
| 128383 | 128376 | rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); |
| 128384 | 128377 | sqlite3Error(db, rc); |
| 128385 | 128378 | } |
| 128386 | 128379 | rc = sqlite3ApiExit(db, rc); |
| 128387 | 128380 | sqlite3_mutex_leave(db->mutex); |
| 128388 | 128381 |
| --- 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 @@ | ||
| 105 | 105 | ** |
| 106 | 106 | ** See also: [sqlite3_libversion()], |
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | -#define SQLITE_VERSION "3.8.8.1" | |
| 110 | +#define SQLITE_VERSION "3.8.8.2" | |
| 111 | 111 | #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" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| 118 | 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.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 @@ | ||
| 1125 | 1125 | }, |
| 1126 | 1126 | { "th.sort.desc:after", |
| 1127 | 1127 | "Descending sort column marker", |
| 1128 | 1128 | @ content: '\2191'; |
| 1129 | 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 | + }, | |
| 1130 | 1143 | { 0, |
| 1131 | 1144 | 0, |
| 1132 | 1145 | 0 |
| 1133 | 1146 | } |
| 1134 | 1147 | }; |
| @@ -1149,23 +1162,47 @@ | ||
| 1149 | 1162 | ); |
| 1150 | 1163 | } |
| 1151 | 1164 | } |
| 1152 | 1165 | } |
| 1153 | 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 | + | |
| 1154 | 1190 | /* |
| 1155 | 1191 | ** WEBPAGE: style.css |
| 1156 | 1192 | */ |
| 1157 | 1193 | void page_style_css(void){ |
| 1158 | 1194 | Blob css; |
| 1159 | 1195 | int i; |
| 1160 | 1196 | |
| 1161 | 1197 | 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); | |
| 1163 | 1199 | |
| 1164 | 1200 | /* add special missing definitions */ |
| 1165 | 1201 | 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) ){ | |
| 1167 | 1204 | blob_appendf(&css, "/* %s */\n%s {\n%s}\n", |
| 1168 | 1205 | cssDefaultList[i].comment, |
| 1169 | 1206 | cssDefaultList[i].elementClass, |
| 1170 | 1207 | cssDefaultList[i].value); |
| 1171 | 1208 | } |
| 1172 | 1209 |
| --- 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 @@ | ||
| 313 | 313 | |
| 314 | 314 | fossil_free(zTag); |
| 315 | 315 | getAllTicketFields(); |
| 316 | 316 | if( haveTicket==0 ) return; |
| 317 | 317 | tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid); |
| 318 | + search_doc_touch('t', tktid, 0); | |
| 318 | 319 | if( haveTicketChng ){ |
| 319 | 320 | db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid); |
| 320 | 321 | } |
| 321 | 322 | db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid); |
| 322 | 323 | tktid = 0; |
| @@ -691,10 +692,11 @@ | ||
| 691 | 692 | if( !g.perm.NewTkt ){ login_needed(); return; } |
| 692 | 693 | if( P("cancel") ){ |
| 693 | 694 | cgi_redirect("home"); |
| 694 | 695 | } |
| 695 | 696 | style_header("New Ticket"); |
| 697 | + ticket_standard_submenu(T_ALL_BUT(T_NEW)); | |
| 696 | 698 | if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); |
| 697 | 699 | ticket_init(); |
| 698 | 700 | initializeVariablesFromCGI(); |
| 699 | 701 | getAllTicketFields(); |
| 700 | 702 | initializeVariablesFromDb(); |
| @@ -1380,5 +1382,60 @@ | ||
| 1380 | 1382 | (eCmd==set?"set":"add"),zTktUuid); |
| 1381 | 1383 | } |
| 1382 | 1384 | } |
| 1383 | 1385 | } |
| 1384 | 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 | +} | |
| 1385 | 1442 |
| --- 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 @@ | ||
| 389 | 389 | void url_enable_proxy(const char *zMsg){ |
| 390 | 390 | const char *zProxy; |
| 391 | 391 | zProxy = zProxyOpt; |
| 392 | 392 | if( zProxy==0 ){ |
| 393 | 393 | 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) ){ | |
| 395 | 395 | zProxy = fossil_getenv("http_proxy"); |
| 396 | 396 | } |
| 397 | 397 | } |
| 398 | 398 | if( zProxy && zProxy[0] && !is_false(zProxy) |
| 399 | 399 | && !g.url.isSsh && !g.url.isFile ){ |
| 400 | 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_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 @@ | ||
| 198 | 198 | if( localUser ){ |
| 199 | 199 | return 0; |
| 200 | 200 | } |
| 201 | 201 | return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1; |
| 202 | 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 | +} | |
| 203 | 299 | |
| 204 | 300 | /* |
| 205 | 301 | ** WEBPAGE: wiki |
| 206 | 302 | ** URL: /wiki?name=PAGENAME |
| 207 | 303 | */ |
| @@ -208,10 +304,11 @@ | ||
| 208 | 304 | void wiki_page(void){ |
| 209 | 305 | char *zTag; |
| 210 | 306 | int rid = 0; |
| 211 | 307 | int isSandbox; |
| 212 | 308 | char *zUuid; |
| 309 | + unsigned submenuFlags = W_ALL; | |
| 213 | 310 | Blob wiki; |
| 214 | 311 | Manifest *pWiki = 0; |
| 215 | 312 | const char *zPageName; |
| 216 | 313 | const char *zMimetype = 0; |
| 217 | 314 | char *zBody = mprintf("%s","<i>Empty Page</i>"); |
| @@ -218,63 +315,35 @@ | ||
| 218 | 315 | |
| 219 | 316 | login_check_credentials(); |
| 220 | 317 | if( !g.perm.RdWiki ){ login_needed(); return; } |
| 221 | 318 | zPageName = P("name"); |
| 222 | 319 | 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 | - @ <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 | + } | |
| 260 | 325 | return; |
| 261 | 326 | } |
| 262 | 327 | if( check_name(zPageName) ) return; |
| 263 | 328 | isSandbox = is_sandbox(zPageName); |
| 264 | 329 | if( isSandbox ){ |
| 330 | + submenuFlags &= ~W_SANDBOX; | |
| 265 | 331 | zBody = db_get("sandbox",zBody); |
| 266 | 332 | zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 267 | 333 | rid = 0; |
| 268 | 334 | }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 | + } | |
| 276 | 345 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 277 | 346 | if( pWiki ){ |
| 278 | 347 | zBody = pWiki->zWiki; |
| 279 | 348 | zMimetype = pWiki->zMimetype; |
| 280 | 349 | } |
| @@ -314,10 +383,11 @@ | ||
| 314 | 383 | g.zTop, zPageName); |
| 315 | 384 | } |
| 316 | 385 | } |
| 317 | 386 | style_set_current_page("%T?name=%T", g.zPath, zPageName); |
| 318 | 387 | style_header("%s", zPageName); |
| 388 | + wiki_standard_submenu(submenuFlags); | |
| 319 | 389 | blob_init(&wiki, zBody, -1); |
| 320 | 390 | wiki_render_by_mimetype(&wiki, zMimetype); |
| 321 | 391 | blob_reset(&wiki); |
| 322 | 392 | attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>"); |
| 323 | 393 | manifest_destroy(pWiki); |
| @@ -570,10 +640,11 @@ | ||
| 570 | 640 | }else{ |
| 571 | 641 | cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); |
| 572 | 642 | } |
| 573 | 643 | } |
| 574 | 644 | style_header("Create A New Wiki Page"); |
| 645 | + wiki_standard_submenu(W_ALL_BUT(W_NEW)); | |
| 575 | 646 | @ <p>Rules for wiki page names:</p> |
| 576 | 647 | well_formed_wiki_name_rules(); |
| 577 | 648 | form_begin(0, "%R/wikinew"); |
| 578 | 649 | @ <p>Name of new wiki page: |
| 579 | 650 | @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> |
| @@ -846,11 +917,12 @@ | ||
| 846 | 917 | */ |
| 847 | 918 | void wiki_prepare_page_list( Stmt * pStmt ){ |
| 848 | 919 | db_prepare(pStmt, |
| 849 | 920 | "SELECT" |
| 850 | 921 | " 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" | |
| 852 | 924 | " FROM tag WHERE tagname GLOB 'wiki-*'" |
| 853 | 925 | " ORDER BY lower(tagname) /*sort*/" |
| 854 | 926 | ); |
| 855 | 927 | } |
| 856 | 928 | /* |
| @@ -870,10 +942,11 @@ | ||
| 870 | 942 | if( showAll ){ |
| 871 | 943 | style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop); |
| 872 | 944 | }else{ |
| 873 | 945 | style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop); |
| 874 | 946 | } |
| 947 | + wiki_standard_submenu(W_ALL_BUT(W_LIST)); | |
| 875 | 948 | @ <ul> |
| 876 | 949 | wiki_prepare_page_list(&q); |
| 877 | 950 | while( db_step(&q)==SQLITE_ROW ){ |
| 878 | 951 | const char *zName = db_column_text(&q, 0); |
| 879 | 952 | int size = db_column_int(&q, 1); |
| @@ -935,11 +1008,12 @@ | ||
| 935 | 1008 | @ </ol> |
| 936 | 1009 | @ <p>We call the first five rules above "wiki" formatting rules. The |
| 937 | 1010 | @ last two rules are the HTML formatting rule.</p> |
| 938 | 1011 | @ <h2>Formatting Rule Details</h2> |
| 939 | 1012 | @ <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 | |
| 941 | 1015 | @ a paragraph break. Centered or right-justified paragraphs are not |
| 942 | 1016 | @ supported by wiki markup, but you can do these things if you need them |
| 943 | 1017 | @ using HTML.</p></li> |
| 944 | 1018 | @ <li> <p><span class="wikiruleHead">Bullet Lists</span>. |
| 945 | 1019 | @ A bullet list item is a line that begins with a single "*" character |
| @@ -960,11 +1034,12 @@ | ||
| 960 | 1034 | @ Text within square brackets ("[...]") becomes a hyperlink. The |
| 961 | 1035 | @ target can be a wiki page name, the artifact ID of a check-in or ticket, |
| 962 | 1036 | @ the name of an image, or a URL. By default, the target is displayed |
| 963 | 1037 | @ as the text of the hyperlink. But you can specify alternative text |
| 964 | 1038 | @ 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 | |
| 966 | 1041 | @ you have added the necessary "<a name='anchor-name'></a>" |
| 967 | 1042 | @ tag to your wiki page.</p></li> |
| 968 | 1043 | @ <li> <p><span class="wikiruleHead">HTML</span>. |
| 969 | 1044 | @ The following standard HTML elements may be used: |
| 970 | 1045 | show_allowed_wiki_markup(); |
| @@ -1166,11 +1241,12 @@ | ||
| 1166 | 1241 | }else if( strncmp(g.argv[2],"delete",n)==0 ){ |
| 1167 | 1242 | if( g.argc!=5 ){ |
| 1168 | 1243 | usage("delete PAGENAME"); |
| 1169 | 1244 | } |
| 1170 | 1245 | 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 )){ | |
| 1172 | 1248 | Stmt q; |
| 1173 | 1249 | db_prepare(&q, |
| 1174 | 1250 | "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'" |
| 1175 | 1251 | " ORDER BY lower(tagname) /*sort*/" |
| 1176 | 1252 | ); |
| 1177 | 1253 |
| --- 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 | @ <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 "<a name='anchor-name'></a>" |
| 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 "<a name='anchor-name'></a>" |
| 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 |
+72
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -2092,9 +2092,81 @@ | ||
| 2092 | 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | 2094 | blob_zero(&out); |
| 2095 | 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | 2096 | blob_reset(&in); |
| 2097 | + fossil_puts(blob_str(&out), 0); | |
| 2098 | + blob_reset(&out); | |
| 2099 | + } | |
| 2100 | +} | |
| 2101 | + | |
| 2102 | +/* | |
| 2103 | +** Remove all HTML markup from the input text. The output written into | |
| 2104 | +** pOut is pure text. | |
| 2105 | +*/ | |
| 2106 | +void html_to_plaintext(const char *zIn, Blob *pOut){ | |
| 2107 | + int n; | |
| 2108 | + int i, j; | |
| 2109 | + int nNL = 0; /* Number of \n characters at the end of pOut */ | |
| 2110 | + int nWS = 0; /* True if pOut ends with whitespace */ | |
| 2111 | + while( zIn[0] ){ | |
| 2112 | + n = nextHtmlToken(zIn); | |
| 2113 | + if( zIn[0]=='<' && n>1 ){ | |
| 2114 | + int isCloseTag; | |
| 2115 | + int eTag; | |
| 2116 | + int eType; | |
| 2117 | + char zTag[32]; | |
| 2118 | + isCloseTag = zIn[1]=='/'; | |
| 2119 | + for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ | |
| 2120 | + zTag[i] = fossil_tolower(zIn[j]); | |
| 2121 | + } | |
| 2122 | + zTag[i] = 0; | |
| 2123 | + eTag = findTag(zTag); | |
| 2124 | + eType = aMarkup[eTag].iType; | |
| 2125 | + if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){ | |
| 2126 | + zIn += n; | |
| 2127 | + while( zIn[0] ){ | |
| 2128 | + n = nextHtmlToken(zIn); | |
| 2129 | + if( fossil_strnicmp(zIn, "</style",7)==0 ) break; | |
| 2130 | + zIn += n; | |
| 2131 | + } | |
| 2132 | + if( zIn[0]=='<' ) zIn += n; | |
| 2133 | + continue; | |
| 2134 | + } | |
| 2135 | + if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ | |
| 2136 | + if( nNL==0 ){ | |
| 2137 | + blob_append(pOut, "\n", 1); | |
| 2138 | + nNL++; | |
| 2139 | + } | |
| 2140 | + nWS = 1; | |
| 2141 | + } | |
| 2142 | + }else if( fossil_isspace(zIn[0]) ){ | |
| 2143 | + for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; | |
| 2144 | + if( !nWS ){ | |
| 2145 | + blob_append(pOut, nNL ? "\n" : " ", 1); | |
| 2146 | + nWS = 1; | |
| 2147 | + } | |
| 2148 | + }else{ | |
| 2149 | + blob_append(pOut, zIn, n); | |
| 2150 | + nNL = nWS = 0; | |
| 2151 | + } | |
| 2152 | + zIn += n; | |
| 2153 | + } | |
| 2154 | + if( nNL==0 ) blob_append(pOut, "\n", 1); | |
| 2155 | +} | |
| 2156 | + | |
| 2157 | +/* | |
| 2158 | +** COMMAND: test-html-to-text | |
| 2159 | +*/ | |
| 2160 | +void test_html_to_text(void){ | |
| 2161 | + Blob in, out; | |
| 2162 | + int i; | |
| 2163 | + | |
| 2164 | + for(i=2; i<g.argc; i++){ | |
| 2165 | + blob_read_from_file(&in, g.argv[i]); | |
| 2166 | + blob_zero(&out); | |
| 2167 | + html_to_plaintext(blob_str(&in), &out); | |
| 2168 | + blob_reset(&in); | |
| 2097 | 2169 | fossil_puts(blob_str(&out), 0); |
| 2098 | 2170 | blob_reset(&out); |
| 2099 | 2171 | } |
| 2100 | 2172 | } |
| 2101 | 2173 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2092,9 +2092,81 @@ | |
| 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | blob_zero(&out); |
| 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | blob_reset(&in); |
| 2097 | fossil_puts(blob_str(&out), 0); |
| 2098 | blob_reset(&out); |
| 2099 | } |
| 2100 | } |
| 2101 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2092,9 +2092,81 @@ | |
| 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | blob_zero(&out); |
| 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | blob_reset(&in); |
| 2097 | fossil_puts(blob_str(&out), 0); |
| 2098 | blob_reset(&out); |
| 2099 | } |
| 2100 | } |
| 2101 | |
| 2102 | /* |
| 2103 | ** Remove all HTML markup from the input text. The output written into |
| 2104 | ** pOut is pure text. |
| 2105 | */ |
| 2106 | void html_to_plaintext(const char *zIn, Blob *pOut){ |
| 2107 | int n; |
| 2108 | int i, j; |
| 2109 | int nNL = 0; /* Number of \n characters at the end of pOut */ |
| 2110 | int nWS = 0; /* True if pOut ends with whitespace */ |
| 2111 | while( zIn[0] ){ |
| 2112 | n = nextHtmlToken(zIn); |
| 2113 | if( zIn[0]=='<' && n>1 ){ |
| 2114 | int isCloseTag; |
| 2115 | int eTag; |
| 2116 | int eType; |
| 2117 | char zTag[32]; |
| 2118 | isCloseTag = zIn[1]=='/'; |
| 2119 | for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ |
| 2120 | zTag[i] = fossil_tolower(zIn[j]); |
| 2121 | } |
| 2122 | zTag[i] = 0; |
| 2123 | eTag = findTag(zTag); |
| 2124 | eType = aMarkup[eTag].iType; |
| 2125 | if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){ |
| 2126 | zIn += n; |
| 2127 | while( zIn[0] ){ |
| 2128 | n = nextHtmlToken(zIn); |
| 2129 | if( fossil_strnicmp(zIn, "</style",7)==0 ) break; |
| 2130 | zIn += n; |
| 2131 | } |
| 2132 | if( zIn[0]=='<' ) zIn += n; |
| 2133 | continue; |
| 2134 | } |
| 2135 | if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ |
| 2136 | if( nNL==0 ){ |
| 2137 | blob_append(pOut, "\n", 1); |
| 2138 | nNL++; |
| 2139 | } |
| 2140 | nWS = 1; |
| 2141 | } |
| 2142 | }else if( fossil_isspace(zIn[0]) ){ |
| 2143 | for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; |
| 2144 | if( !nWS ){ |
| 2145 | blob_append(pOut, nNL ? "\n" : " ", 1); |
| 2146 | nWS = 1; |
| 2147 | } |
| 2148 | }else{ |
| 2149 | blob_append(pOut, zIn, n); |
| 2150 | nNL = nWS = 0; |
| 2151 | } |
| 2152 | zIn += n; |
| 2153 | } |
| 2154 | if( nNL==0 ) blob_append(pOut, "\n", 1); |
| 2155 | } |
| 2156 | |
| 2157 | /* |
| 2158 | ** COMMAND: test-html-to-text |
| 2159 | */ |
| 2160 | void test_html_to_text(void){ |
| 2161 | Blob in, out; |
| 2162 | int i; |
| 2163 | |
| 2164 | for(i=2; i<g.argc; i++){ |
| 2165 | blob_read_from_file(&in, g.argv[i]); |
| 2166 | blob_zero(&out); |
| 2167 | html_to_plaintext(blob_str(&in), &out); |
| 2168 | blob_reset(&in); |
| 2169 | fossil_puts(blob_str(&out), 0); |
| 2170 | blob_reset(&out); |
| 2171 | } |
| 2172 | } |
| 2173 |
+1
-1
| --- win/Makefile.PellesCGMake | ||
| +++ win/Makefile.PellesCGMake | ||
| @@ -83,11 +83,11 @@ | ||
| 83 | 83 | |
| 84 | 84 | # define the SQLite files, which need special flags on compile |
| 85 | 85 | SQLITESRC=sqlite3.c |
| 86 | 86 | ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) |
| 87 | 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 | |
| 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 | 89 | |
| 90 | 90 | # define the SQLite shell files, which need special flags on compile |
| 91 | 91 | SQLITESHELLSRC=shell.c |
| 92 | 92 | ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf)) |
| 93 | 93 | SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj)) |
| 94 | 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_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 |
+1
-1
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | CFLAGS = -o |
| 25 | 25 | BCC = $(DMDIR)\bin\dmc $(CFLAGS) |
| 26 | 26 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) |
| 27 | 27 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 |
| 28 | 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 | |
| 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 | 30 | |
| 31 | 31 | SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | 32 | |
| 33 | 33 | SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | 34 | |
| 35 | 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 |
| 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 |
+2
-1
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | 150 | # |
| 151 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l | |
| 151 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 152 | 152 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 153 | 153 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 154 | 154 | |
| 155 | 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 156 | 156 | # source code directory resides (depending on the value of the macro |
| @@ -2024,10 +2024,11 @@ | ||
| 2024 | 2024 | -DSQLITE_ENABLE_LOCKING_STYLE=0 \ |
| 2025 | 2025 | -DSQLITE_THREADSAFE=0 \ |
| 2026 | 2026 | -DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 2027 | 2027 | -DSQLITE_OMIT_DEPRECATED \ |
| 2028 | 2028 | -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ |
| 2029 | + -DSQLITE_ENABLE_FTS4 \ | |
| 2029 | 2030 | -DSQLITE_WIN32_NO_ANSI \ |
| 2030 | 2031 | -D_HAVE__MINGW_H \ |
| 2031 | 2032 | -DSQLITE_USE_MALLOC_H \ |
| 2032 | 2033 | -DSQLITE_USE_MSIZE |
| 2033 | 2034 | |
| 2034 | 2035 |
| --- 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 |
+1
-1
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | 150 | # |
| 151 | -OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l | |
| 151 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 152 | 152 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 153 | 153 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 154 | 154 | |
| 155 | 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 156 | 156 | # source code directory resides (depending on the value of the macro |
| 157 | 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.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 |
+2
-1
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -55,11 +55,11 @@ | ||
| 55 | 55 | |
| 56 | 56 | # Uncomment to enable Tcl support |
| 57 | 57 | # FOSSIL_ENABLE_TCL = 1 |
| 58 | 58 | |
| 59 | 59 | !ifdef FOSSIL_ENABLE_SSL |
| 60 | -SSLDIR = $(B)\compat\openssl-1.0.1l | |
| 60 | +SSLDIR = $(B)\compat\openssl-1.0.2 | |
| 61 | 61 | SSLINCDIR = $(SSLDIR)\inc32 |
| 62 | 62 | SSLLIBDIR = $(SSLDIR)\out32 |
| 63 | 63 | SSLLFLAGS = /nologo /opt:ref /debug |
| 64 | 64 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 65 | 65 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -189,10 +189,11 @@ | ||
| 189 | 189 | /DSQLITE_ENABLE_LOCKING_STYLE=0 \ |
| 190 | 190 | /DSQLITE_THREADSAFE=0 \ |
| 191 | 191 | /DSQLITE_DEFAULT_FILE_FORMAT=4 \ |
| 192 | 192 | /DSQLITE_OMIT_DEPRECATED \ |
| 193 | 193 | /DSQLITE_ENABLE_EXPLAIN_COMMENTS \ |
| 194 | + /DSQLITE_ENABLE_FTS4 \ | |
| 194 | 195 | /DSQLITE_WIN32_NO_ANSI |
| 195 | 196 | |
| 196 | 197 | SHELL_OPTIONS = /Dmain=sqlite3_shell \ |
| 197 | 198 | /DSQLITE_OMIT_LOAD_EXTENSION=1 \ |
| 198 | 199 | /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ |
| 199 | 200 |
| --- 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 @@ | ||
| 122 | 122 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 123 | 123 | first <a href="https://www.openssl.org/source/">download the official |
| 124 | 124 | source code for OpenSSL</a> and extract it to an appropriately named |
| 125 | 125 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 126 | 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | -"<b>compat/openssl-1.0.1l</b>"), then make sure that some recent | |
| 127 | +"<b>compat/openssl-1.0.2</b>"), then make sure that some recent | |
| 128 | 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | 129 | and finally run one of the following commands: |
| 130 | 130 | <blockquote><pre> |
| 131 | 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | 132 | </pre></blockquote> |
| 133 | 133 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -122,11 +122,11 @@ | |
| 122 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 123 | first <a href="https://www.openssl.org/source/">download the official |
| 124 | source code for OpenSSL</a> and extract it to an appropriately named |
| 125 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | "<b>compat/openssl-1.0.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 |
+20
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,7 +1,27 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 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 | + | |
| 3 | 23 | <h2>Changes For Version 1.30 (2015-01-19)</h2> |
| 4 | 24 | * Added the [/help?cmd=bundle|fossil bundle] command. |
| 5 | 25 | * Added the [/help?cmd=purge|fossil purge] command. |
| 6 | 26 | * Added the [/help?cmd=publish|fossil publish] command. |
| 7 | 27 | * Added the [/help?cmd=unpublished|fossil unpublished] command. |
| 8 | 28 |
| --- 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 @@ | ||
| 23 | 23 | <li> [/timeline | Recent changes] |
| 24 | 24 | <li> [./faq.wiki | FAQ] |
| 25 | 25 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 26 | 26 | <li> [./changes.wiki | Change Log] |
| 27 | 27 | <li> [./hints.wiki | Tip & Hints] |
| 28 | -<li> [./permutedindex.wiki | Documentation Index] | |
| 28 | +<li> [./permutedindex.html | Documentation Index] | |
| 29 | 29 | <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book] |
| 30 | 30 | <li> Mailing list |
| 31 | 31 | <ul> |
| 32 | 32 | <li> [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | sign-up] |
| 33 | 33 | <li> [http://www.mail-archive.com/[email protected] | archives] |
| 34 | 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 & 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 & 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 |
+7
-6
| --- www/makefile.wiki | ||
| +++ www/makefile.wiki | ||
| @@ -224,21 +224,22 @@ | ||
| 224 | 224 | |
| 225 | 225 | Some files require special C-preprocessor macro definitions. |
| 226 | 226 | When compiling sqlite.c, the following macros are recommended: |
| 227 | 227 | |
| 228 | 228 | * -DSQLITE_OMIT_LOAD_EXTENSION=1 |
| 229 | + * -DSQLITE_ENABLE_FTS4=1 | |
| 229 | 230 | * -DSQLITE_ENABLE_LOCKING_STYLE=0 |
| 230 | 231 | * -DSQLITE_THREADSAFE=0 |
| 231 | 232 | * -DSQLITE_DEFAULT_FILE_FORMAT=4 |
| 232 | 233 | * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 |
| 233 | 234 | |
| 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. | |
| 240 | 241 | |
| 241 | 242 | When compiling the shell.c source file, these macros are required: |
| 242 | 243 | |
| 243 | 244 | * -Dmain=sqlite3_main |
| 244 | 245 | * -DSQLITE_OMIT_LOAD_EXTENSION=1 |
| 245 | 246 |
| --- 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 |
+1
-1
| --- www/mkdownload.tcl | ||
| +++ www/mkdownload.tcl | ||
| @@ -26,11 +26,11 @@ | ||
| 26 | 26 | <div class="mainmenu"> |
| 27 | 27 | <a href='/fossil/doc/trunk/www/index.wiki'>Home</a> |
| 28 | 28 | <a href='/fossil/timeline?y=ci'>Timeline</a> |
| 29 | 29 | <a href='/download.html'>Download</a> |
| 30 | 30 | <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> | |
| 32 | 32 | <a href='/fossil/brlist'>Branches</a> |
| 33 | 33 | <a href='/fossil/taglist'>Tags</a> |
| 34 | 34 | <a href='/fossil/reportlist'>Tickets</a> |
| 35 | 35 | </div> |
| 36 | 36 | <div class="content"> |
| 37 | 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.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 @@ | ||
| 1 | 1 | #!/bin/sh |
| 2 | 2 | # |
| 3 | 3 | # Run this TCL script to generate a WIKI page that contains a |
| 4 | 4 | # permuted index of the various documentation files. |
| 5 | 5 | # |
| 6 | -# tclsh mkindex.tcl >permutedindex.wiki | |
| 6 | +# tclsh mkindex.tcl >permutedindex.html | |
| 7 | 7 | # |
| 8 | 8 | |
| 9 | 9 | set doclist { |
| 10 | 10 | adding_code.wiki {Adding New Features To Fossil} |
| 11 | 11 | adding_code.wiki {Hacking Fossil} |
| @@ -77,26 +77,34 @@ | ||
| 77 | 77 | lappend permindex [list "$suffix — $prefix" $file] |
| 78 | 78 | } |
| 79 | 79 | } |
| 80 | 80 | } |
| 81 | 81 | set permindex [lsort -dict -index 0 $permindex] |
| 82 | -set out [open permutedindex.wiki w] | |
| 82 | +set out [open permutedindex.html w] | |
| 83 | 83 | 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'>" | |
| 85 | 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> | |
| 86 | 93 | <h2>Primary Documents:</h2> |
| 87 | 94 | <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> | |
| 94 | 102 | </ul> |
| 95 | 103 | <a name="pindex"></a> |
| 96 | 104 | <h2>Permuted Index:</h2> |
| 97 | 105 | <ul>} |
| 98 | 106 | foreach entry $permindex { |
| 99 | 107 | foreach {title file} $entry break |
| 100 | 108 | puts $out "<li><a href=\"$file\">$title</a></li>" |
| 101 | 109 | } |
| 102 | -puts $out "</ul>" | |
| 110 | +puts $out "</ul></div>" | |
| 103 | 111 | |
| 104 | 112 | ADDED www/permutedindex.html |
| 105 | 113 | 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 — $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 — $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 |
+3
-6
| --- 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> | |
| 6 | 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> |
| 7 | 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> |
| 8 | 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 @@ | ||
| 10 | 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> |
| 11 | 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> |
| 12 | 9 | </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 |
+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 |
| --- 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 @@ | |
+1
-1
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -387,9 +387,9 @@ | ||
| 387 | 387 | |
| 388 | 388 | <h2>More Hints</h2> |
| 389 | 389 | |
| 390 | 390 | <p>A [/help | complete list of commands] is available, as is the |
| 391 | 391 | [./hints.wiki|helpful hints] document. See the |
| 392 | - [./permutedindex.wiki#pindex|permuted index] for additional | |
| 392 | + [./permutedindex.html#pindex|permuted index] for additional | |
| 393 | 393 | documentation. |
| 394 | 394 | |
| 395 | 395 | <p>Explore and have fun!</p> |
| 396 | 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.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 @@ | ||
| 384 | 384 | |
| 385 | 385 | <blockquote> |
| 386 | 386 | <b>reqconfig</b> <i>configuration-name</i> |
| 387 | 387 | </blockquote> |
| 388 | 388 | |
| 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 | |
| 390 | 390 | following values: |
| 391 | 391 | |
| 392 | 392 | <center><table border=0> |
| 393 | 393 | <tr><td valign="top"> |
| 394 | 394 | <ul> |
| @@ -395,25 +395,42 @@ | ||
| 395 | 395 | <li> css |
| 396 | 396 | <li> header |
| 397 | 397 | <li> footer |
| 398 | 398 | <li> logo-mimetype |
| 399 | 399 | <li> logo-image |
| 400 | -<li> project-name | |
| 401 | -<li> project-description | |
| 402 | -<li> manifest | |
| 400 | +<li> background-mimetype | |
| 401 | +<li> background-image | |
| 403 | 402 | <li> index-page |
| 404 | -<ul></td><td valign="top"><ul> | |
| 405 | 403 | <li> timeline-block-markup |
| 406 | 404 | <li> timeline-max-comment |
| 407 | 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 | |
| 408 | 425 | <li> ticket-table |
| 409 | 426 | <li> ticket-common |
| 410 | 427 | <li> ticket-change |
| 411 | 428 | <li> ticket-newpage |
| 412 | 429 | <li> ticket-viewpage |
| 413 | -<li> ticket-editpage | |
| 414 | 430 | <ul></td><td valign="top"><ul> |
| 431 | +<li> ticket-editpage | |
| 415 | 432 | <li> ticket-reportlist |
| 416 | 433 | <li> ticket-report-template |
| 417 | 434 | <li> ticket-key-template |
| 418 | 435 | <li> ticket-title-expr |
| 419 | 436 | <li> ticket-closed-expr |
| 420 | 437 |
| --- 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 |